no thoughts, head segfaulted

This commit is contained in:
Letter N
2020-08-15 16:48:10 +08:00
parent 15e659fc9c
commit a327e7d33f
15 changed files with 628 additions and 71 deletions

View File

@@ -39,6 +39,10 @@ export const loadSourceMaps = async bundleDir => {
}; };
export const retrace = stack => { export const retrace = stack => {
if (typeof stack !== 'string') {
logger.log('ERROR: Stack is not a string!', stack);
return stack;
}
const header = stack.split(/\n\s.*at/)[0]; const header = stack.split(/\n\s.*at/)[0];
const mappedStack = StackTraceParser.parse(stack) const mappedStack = StackTraceParser.parse(stack)
.map(frame => { .map(frame => {

View File

@@ -0,0 +1,127 @@
import { map, sortBy } from 'common/collections';
import { flow } from 'common/fp';
import { useBackend } from '../backend';
import { Box, Button, Flex, Section, Table } from '../components';
import { Window } from '../layouts';
export const AtmosControlPanel = (props, context) => {
const { act, data } = useBackend(context);
const groups = flow([
map((group, i) => ({
...group,
// Generate a unique id
id: group.area + i,
})),
sortBy(group => group.id),
])(data.excited_groups);
return (
<Window
title="SSAir Control Panel"
width={900}
height={500}
resizable>
<Section m={1}>
<Flex
justify="space-between"
align="baseline">
<Flex.Item>
<Button
onClick={() => act('toggle-freeze')}
color={data.frozen === 1 ? 'good' : 'bad'}>
{data.frozen === 1
? 'Freeze Subsystem'
: 'Unfreeze Subsystem'}
</Button>
</Flex.Item>
<Flex.Item>
Fire Cnt: {data.fire_count}
</Flex.Item>
<Flex.Item>
Active Turfs: {data.active_size}
</Flex.Item>
<Flex.Item>
Excited Groups: {data.excited_size}
</Flex.Item>
<Flex.Item>
Hotspots: {data.hotspots_size}
</Flex.Item>
<Flex.Item>
Superconductors: {data.conducting_size}
</Flex.Item>
<Flex.Item>
<Button.Checkbox
checked={data.showing_user}
onClick={() => act('toggle_user_display')}>
Personal View
</Button.Checkbox>
</Flex.Item>
<Flex.Item>
<Button.Checkbox
checked={data.show_all}
onClick={() => act('toggle_show_all')}>
Display all
</Button.Checkbox>
</Flex.Item>
</Flex>
</Section>
<Box fillPositionedParent top="45px">
<Window.Content scrollable>
<Section>
<Table>
<Table.Row header>
<Table.Cell>
Area Name
</Table.Cell>
<Table.Cell collapsing>
Breakdown
</Table.Cell>
<Table.Cell collapsing>
Dismantle
</Table.Cell>
<Table.Cell collapsing>
Turfs
</Table.Cell>
<Table.Cell collapsing>
{data.display_max === 1 && "Max Share"}
</Table.Cell>
<Table.Cell collapsing>
Display
</Table.Cell>
</Table.Row>
{groups.map(group => (
<tr key={group.id}>
<td>
<Button
content={group.area}
onClick={() => act('move-to-target', {
spot: group.jump_to,
})} />
</td>
<td>
{group.breakdown}
</td>
<td>
{group.dismantle}
</td>
<td>
{group.size}
</td>
<td>
{data.display_max === 1 && group.max_share}
</td>
<td>
<Button.Checkbox
checked={group.should_show}
onClick={() => act('toggle_show_group', {
group: group.group,
})} />
</td>
</tr>
))}
</Table>
</Section>
</Window.Content>
</Box>
</Window>
);
};

View File

@@ -5,7 +5,7 @@ import { createSearch } from 'common/string';
import { Fragment } from 'inferno'; import { Fragment } from 'inferno';
import { useBackend, useLocalState } from '../backend'; import { useBackend, useLocalState } from '../backend';
import { Button, ByondUi, Input, Section } from '../components'; import { Button, ByondUi, Input, Section } from '../components';
import { refocusLayout, Window } from '../layouts'; import { Window } from '../layouts';
/** /**
* Returns previous and next camera names relative to the currently * Returns previous and next camera names relative to the currently
@@ -123,12 +123,9 @@ export const CameraConsoleContent = (props, context) => {
&& camera.name === activeCamera.name && camera.name === activeCamera.name
&& 'Button--selected', && 'Button--selected',
])} ])}
onClick={() => { onClick={() => act('switch_camera', {
refocusLayout(); name: camera.name,
act('switch_camera', { })}>
name: camera.name,
});
}}>
{camera.name} {camera.name}
</div> </div>
))} ))}

View File

@@ -20,34 +20,36 @@ export const Cargo = (props, context) => {
resizable> resizable>
<Window.Content scrollable> <Window.Content scrollable>
<CargoStatus /> <CargoStatus />
<Tabs> <Section fitted>
<Tabs.Tab <Tabs>
icon="list"
selected={tab === 'catalog'}
onClick={() => setTab('catalog')}>
Catalog
</Tabs.Tab>
<Tabs.Tab
icon="envelope"
textColor={tab !== 'requests'
&& requests.length > 0
&& 'yellow'}
selected={tab === 'requests'}
onClick={() => setTab('requests')}>
Requests ({requests.length})
</Tabs.Tab>
{!requestonly && (
<Tabs.Tab <Tabs.Tab
icon="shopping-cart" icon="list"
textColor={tab !== 'cart' selected={tab === 'catalog'}
&& cart.length > 0 onClick={() => setTab('catalog')}>
&& 'yellow'} Catalog
selected={tab === 'cart'}
onClick={() => setTab('cart')}>
Checkout ({cart.length})
</Tabs.Tab> </Tabs.Tab>
)} <Tabs.Tab
</Tabs> icon="envelope"
textColor={tab !== 'requests'
&& requests.length > 0
&& 'yellow'}
selected={tab === 'requests'}
onClick={() => setTab('requests')}>
Requests ({requests.length})
</Tabs.Tab>
{!requestonly && (
<Tabs.Tab
icon="shopping-cart"
textColor={tab !== 'cart'
&& cart.length > 0
&& 'yellow'}
selected={tab === 'cart'}
onClick={() => setTab('cart')}>
Checkout ({cart.length})
</Tabs.Tab>
)}
</Tabs>
</Section>
{tab === 'catalog' && ( {tab === 'catalog' && (
<CargoCatalog /> <CargoCatalog />
)} )}
@@ -143,7 +145,7 @@ export const CargoCatalog = (props, context) => {
</Fragment> </Fragment>
)}> )}>
<Flex> <Flex>
<Flex.Item> <Flex.Item ml={-1} mr={1}>
<Tabs vertical> <Tabs vertical>
{supplies.map(supply => ( {supplies.map(supply => (
<Tabs.Tab <Tabs.Tab

View File

@@ -242,13 +242,14 @@ const CfStep2 = (props, context) => {
</Table.Row> </Table.Row>
<Table.Row> <Table.Row>
<Table.Cell bold position="relative"> <Table.Cell bold position="relative">
Card Reader: Secondary Card Reader:
<Tooltip <Tooltip
content={multiline` content={multiline`
Adds a slot that allows you to manipulate RFID cards. Adds a secondary RFID card reader, for manipulating or
Please note that this is not necessary to allow the device reading from a second standard RFID card.
to read your identification, it is just necessary to Please note that a primary card reader is necessary to
manipulate other cards. allow the device to read your identification, but one
is included in the base price.
`} `}
position="right" /> position="right" />
</Table.Cell> </Table.Cell>

View File

@@ -0,0 +1,102 @@
import { useBackend } from '../backend';
import { Button, Flex, Fragment, Section, NoticeBox } from '../components';
import { Window } from '../layouts';
export const GhostPoolProtection = (props, context) => {
const { act, data } = useBackend(context);
const {
events_or_midrounds,
spawners,
station_sentience,
silicons,
minigames,
} = data;
return (
<Window
title="Ghost Pool Protection"
width={400}
height={270}>
<Window.Content>
<Flex grow={1} height="100%">
<Section
title="Options"
buttons={
<Fragment>
<Button
color="good"
icon="plus-circle"
content="Enable Everything"
onClick={() => act("all_roles")} />
<Button
color="bad"
icon="minus-circle"
content="Disable Everything"
onClick={() => act("no_roles")} />
</Fragment>
}>
<NoticeBox danger>
For people creating a sneaky event: If you
toggle Station Created Sentience, people may
catch on that admins have disabled roles for
your event...
</NoticeBox>
<Flex.Item>
<Button
fluid
textAlign="center"
color={events_or_midrounds ? "good" : "bad"}
icon="meteor"
content="Events and Midround Rulesets"
onClick={() => act("toggle_events_or_midrounds")} />
</Flex.Item>
<Flex.Item>
<Button
fluid
textAlign="center"
color={spawners ? "good" : "bad"}
icon="pastafarianism"
content="Ghost Role Spawners"
onClick={() => act("toggle_spawners")} />
</Flex.Item>
<Flex.Item>
<Button
fluid
textAlign="center"
color={station_sentience ? "good" : "bad"}
icon="user-astronaut"
content="Station Created Sentience"
onClick={() => act("toggle_station_sentience")} />
</Flex.Item>
<Flex.Item>
<Button
fluid
textAlign="center"
color={silicons ? "good" : "bad"}
icon="robot"
content="Silicons"
onClick={() => act("toggle_silicons")} />
</Flex.Item>
<Flex.Item>
<Button
fluid
textAlign="center"
color={minigames ? "good" : "bad"}
icon="gamepad"
content="Minigames"
onClick={() => act("toggle_minigames")} />
</Flex.Item>
<Flex.Item>
<Button
fluid
textAlign="center"
color="orange"
icon="check"
content="Apply Changes"
onClick={() => act("apply_settings")} />
</Flex.Item>
</Section>
</Flex>
</Window.Content>
</Window>
);
};

View File

@@ -0,0 +1,97 @@
import { useBackend } from '../backend';
import { Box, Button, Divider, Flex, Grid, Input, NoticeBox, NumberInput, Section } from '../components';
import { Window } from '../layouts';
export const MechpadControl = (props, context) => {
const { topLevel } = props;
const { act, data } = useBackend(context);
const {
pad_name,
connected_mechpad,
} = data;
return (
<Section
title={(
<Input
value={pad_name}
width="170px"
onChange={(e, value) => act('rename', {
name: value,
})} />
)}
level={topLevel ? 1 : 2}
buttons={(
<Button
icon="times"
content="Remove"
color="bad"
onClick={() => act('remove')} />
)}>
{!connected_mechpad && (
<Box color="bad" textAlign="center">
No Pad Connected.
</Box>
) || (
<Button
fluid
icon="upload"
content="Launch"
textAlign="center"
onClick={() => act('launch')} />
)}
</Section>
);
};
export const MechpadConsole = (props, context) => {
const { act, data } = useBackend(context);
const {
mechpads = [],
selected_id,
} = data;
return (
<Window
width={475}
height={130}
resizable>
<Window.Content>
{mechpads.length === 0 && (
<NoticeBox>
No Pads Connected
</NoticeBox>
) || (
<Section>
<Flex minHeight="70px">
<Flex.Item width="140px" minHeight="70px">
{mechpads.map(mechpad => (
<Button
fluid
ellipsis
key={mechpad.name}
content={mechpad.name}
selected={selected_id === mechpad.id}
color="transparent"
onClick={() => act('select_pad', {
id: mechpad.id,
})} />
))}
</Flex.Item>
<Flex.Item minHeight="100%">
<Divider vertical />
</Flex.Item>
<Flex.Item grow={1} basis={0} minHeight="100%">
{selected_id && (
<MechpadControl />
) || (
<Box>
Please select a pad
</Box>
)}
</Flex.Item>
</Flex>
</Section>
)}
</Window.Content>
</Window>
);
};

View File

@@ -28,6 +28,8 @@ export const NtosMain = (props, context) => {
has_light, has_light,
light_on, light_on,
comp_light_color, comp_light_color,
removable_media = [],
login = [],
} = data; } = data;
return ( return (
<NtosWindow <NtosWindow
@@ -56,6 +58,44 @@ export const NtosMain = (props, context) => {
</Button> </Button>
</Section> </Section>
)} )}
<Section
title="User Login"
buttons={(
<Button
icon="eject"
content="Eject ID"
disabled={!login.IDName}
onClick={() => act('PC_Eject_Disk', { name: "ID" })}
/>
)}>
<Table>
<Table.Row>
ID Name: {login.IDName}
</Table.Row>
<Table.Row>
Assignment: {login.IDJob}
</Table.Row>
</Table>
</Section>
{!!removable_media.length && (
<Section title="Media Eject">
<Table>
{removable_media.map(device => (
<Table.Row key={device}>
<Table.Cell>
<Button
fluid
color="transparent"
icon="eject"
content={device}
onClick={() => act('PC_Eject_Disk', { name: device })}
/>
</Table.Cell>
</Table.Row>
))}
</Table>
</Section>
)}
<Section title="Programs"> <Section title="Programs">
<Table> <Table>
{programs.map(program => ( {programs.map(program => (

View File

@@ -10,7 +10,7 @@ export const NtosRadar = (props, context) => {
width={800} width={800}
height={600} height={600}
theme="ntos"> theme="ntos">
<NtosRadarContent /> <NtosRadarContent sig_err={"Signal Lost"} />
</NtosWindow> </NtosWindow>
); );
}; };
@@ -23,6 +23,7 @@ export const NtosRadarContent = (props, context) => {
target = [], target = [],
scanning, scanning,
} = data; } = data;
const { sig_err } = props;
return ( return (
<Flex <Flex
direction={"row"} direction={"row"}
@@ -89,7 +90,7 @@ export const NtosRadarContent = (props, context) => {
width={42} width={42}
fontSize="30px" fontSize="30px"
textAlign="center"> textAlign="center">
Signal Lost {sig_err}
</NoticeBox> </NoticeBox>
) )
: !!target.userot && ( : !!target.userot && (

View File

@@ -7,7 +7,7 @@ export const NtosRadarSyndicate = (props, context) => {
width={800} width={800}
height={600} height={600}
theme="syndicate"> theme="syndicate">
<NtosRadarContent /> <NtosRadarContent sig_err={"Out of Range"} />
</NtosWindow> </NtosWindow>
); );
}; };

View File

@@ -6,7 +6,7 @@
* @license MIT * @license MIT
*/ */
import { classes, isFalsy } from "common/react"; import { classes } from 'common/react';
import { vecScale, vecSubtract } from 'common/vector'; import { vecScale, vecSubtract } from 'common/vector';
import DOMPurify from 'dompurify'; import DOMPurify from 'dompurify';
import { Component } from 'inferno'; import { Component } from 'inferno';
@@ -241,13 +241,11 @@ const PaperSheetView = (props, context) => {
stamps, stamps,
backgroundColor, backgroundColor,
readOnly, readOnly,
...rest
} = props; } = props;
const readonly = !isFalsy(readOnly);
const stamp_list = stamps || []; const stamp_list = stamps || [];
const text_html = { const text_html = {
__html: '<span class="paper-text">' __html: '<span class="paper-text">'
+ setInputReadonly(value, readonly) + setInputReadonly(value, readOnly)
+ '</span>', + '</span>',
}; };
return ( return (
@@ -323,7 +321,12 @@ class PaperSheetStamper extends Component {
handleMouseClick(e) { handleMouseClick(e) {
const pos = this.findStampPosition(e); const pos = this.findStampPosition(e);
const { act, data } = useBackend(this.context); const { act, data } = useBackend(this.context);
act("stamp", { x: pos[0], y: pos[1], r: this.state.rotate }); const stamp_obj = {
x: pos[0], y: pos[1], r: this.state.rotate,
stamp_class: this.props.stamp_class,
stamp_icon_state: data.stamp_icon_state,
};
act("stamp", stamp_obj);
this.setState({ x: pos[0], y: pos[1] }); this.setState({ x: pos[0], y: pos[1] });
} }
@@ -360,7 +363,7 @@ class PaperSheetStamper extends Component {
onMouseMove={this.handleMouseMove.bind(this)} onMouseMove={this.handleMouseMove.bind(this)}
onwheel={this.handleWheel.bind(this)} {...rest}> onwheel={this.handleWheel.bind(this)} {...rest}>
<PaperSheetView <PaperSheetView
readOnly={1} readOnly
value={value} value={value}
stamps={stamp_list} /> stamps={stamp_list} />
<Stamp <Stamp
@@ -593,7 +596,7 @@ export const PaperSheet = (props, context) => {
<PaperSheetView <PaperSheetView
value={text} value={text}
stamps={stamp_list} stamps={stamp_list}
readOnly={1} /> readOnly />
); );
case 1: case 1:
return ( return (

View File

@@ -0,0 +1,182 @@
import { ProgressBar, NumberInput, Button, Section, Box, Flex } from '../components';
import { useBackend } from '../backend';
import { Window } from '../layouts';
export const Photocopier = (props, context) => {
const { data } = useBackend(context);
const {
isAI,
has_toner,
has_item,
} = data;
return (
<Window
title="Photocopier"
width={240}
height={isAI ? 309 : 234}>
<Window.Content>
{has_toner ? (
<Toner />
) : (
<Section title="Toner">
<Box color="average">
No inserted toner cartridge.
</Box>
</Section>
)}
{has_item ? (
<Options />
) : (
<Section title="Options">
<Box color="average">
No inserted item.
</Box>
</Section>
)}
{!!isAI && (
<AIOptions />
)}
</Window.Content>
</Window>
);
};
const Toner = (props, context) => {
const { act, data } = useBackend(context);
const {
max_toner,
current_toner,
} = data;
const average_toner = max_toner * 0.66;
const bad_toner = max_toner * 0.33;
return (
<Section
title="Toner"
buttons={
<Button
disabled={!current_toner}
onClick={() => act('remove_toner')}
icon="eject">
Eject
</Button>
}>
<ProgressBar
ranges={{
good: [average_toner, max_toner],
average: [bad_toner, average_toner],
bad: [0, bad_toner],
}}
value={current_toner}
minValue={0}
maxValue={max_toner} />
</Section>
);
};
const Options = (props, context) => {
const { act, data } = useBackend(context);
const {
color_mode,
is_photo,
num_copies,
has_enough_toner,
} = data;
return (
<Section title="Options">
<Flex>
<Flex.Item
mt={0.4}
width={11}
color="label">
Make copies:
</Flex.Item>
<Flex.Item>
<NumberInput
animate
width={2.6}
height={1.65}
step={1}
stepPixelSize={8}
minValue={1}
maxValue={10}
value={num_copies}
onDrag={(e, value) => act('set_copies', {
num_copies: value,
})} />
</Flex.Item>
<Flex.Item>
<Button
ml={0.2}
icon="copy"
textAlign="center"
disabled={!has_enough_toner}
onClick={() => act('make_copy')}>
Copy
</Button>
</Flex.Item>
</Flex>
{!!is_photo && (
<Flex mt={0.5}>
<Flex.Item
mr={0.4}
width="50%">
<Button
fluid
textAlign="center"
selected={color_mode === "Greyscale"}
onClick={() => act('color_mode', {
mode: "Greyscale",
})}>
Greyscale
</Button>
</Flex.Item>
<Flex.Item
ml={0.4}
width="50%">
<Button
fluid
textAlign="center"
selected={color_mode === "Color"}
onClick={() => act('color_mode', {
mode: "Color",
})}>
Color
</Button>
</Flex.Item>
</Flex>
)}
<Button
mt={0.5}
textAlign="center"
icon="reply"
fluid
onClick={() => act('remove')}>
Remove item
</Button>
</Section>
);
};
const AIOptions = (props, context) => {
const { act, data } = useBackend(context);
const { can_AI_print } = data;
return (
<Section title="AI Options">
<Box>
<Button
fluid
icon="images"
textAlign="center"
disabled={!can_AI_print}
onClick={() => act('ai_photo')}>
Print photo from database
</Button>
</Box>
</Section>
);
};

View File

@@ -1,17 +1,25 @@
import { Fragment } from 'inferno'; import { Fragment } from 'inferno';
import { useBackend } from '../backend'; import { useBackend } from '../backend';
import { Box, Button, LabeledList, Section } from '../components'; import { Box, Button, LabeledList, Section, NoticeBox } from '../components';
import { Window } from '../layouts'; import { Window } from '../layouts';
export const Wires = (props, context) => { export const Wires = (props, context) => {
const { act, data } = useBackend(context); const { act, data } = useBackend(context);
const { proper_name } = data;
const wires = data.wires || []; const wires = data.wires || [];
const statuses = data.status || []; const statuses = data.status || [];
return ( return (
<Window <Window
width={350} width={350}
height={150 + wires.length * 30}> height={150
+ (wires.length * 30)
+ (!!proper_name && 30)}>
<Window.Content> <Window.Content>
{(!!proper_name && (
<NoticeBox textAlign="center">
{proper_name} Wire Configuration
</NoticeBox>
))}
<Section> <Section>
<LabeledList> <LabeledList>
{wires.map(wire => ( {wires.map(wire => (

View File

@@ -5,29 +5,15 @@
*/ */
import { classes } from 'common/react'; import { classes } from 'common/react';
import { computeBoxProps, computeBoxClassName } from '../components/Box'; import { computeBoxClassName, computeBoxProps } from '../components/Box';
import { addScrollableNode, removeScrollableNode } from '../events';
/**
* Brings Layout__content DOM element back to focus.
*
* Commonly used to keep the content scrollable in IE.
*/
export const refocusLayout = () => {
// IE8: Focus method is seemingly fucked.
if (Byond.IS_LTE_IE8) {
return;
}
const element = document.getElementById('Layout__content');
if (element) {
element.focus();
}
};
export const Layout = props => { export const Layout = props => {
const { const {
className, className,
theme = 'nanotrasen', theme = 'nanotrasen',
children, children,
...rest
} = props; } = props;
return ( return (
<div className={'theme-' + theme}> <div className={'theme-' + theme}>
@@ -35,7 +21,9 @@ export const Layout = props => {
className={classes([ className={classes([
'Layout', 'Layout',
className, className,
])}> ...computeBoxClassName(rest),
])}
{...computeBoxProps(rest)}>
{children} {children}
</div> </div>
</div> </div>
@@ -51,7 +39,6 @@ const LayoutContent = props => {
} = props; } = props;
return ( return (
<div <div
id="Layout__content"
className={classes([ className={classes([
'Layout__content', 'Layout__content',
scrollable && 'Layout__content--scrollable', scrollable && 'Layout__content--scrollable',
@@ -64,4 +51,9 @@ const LayoutContent = props => {
); );
}; };
LayoutContent.defaultHooks = {
onComponentDidMount: node => addScrollableNode(node),
onComponentWillUnmount: node => removeScrollableNode(node),
};
Layout.Content = LayoutContent; Layout.Content = LayoutContent;

View File

@@ -4,6 +4,7 @@
* @license MIT * @license MIT
*/ */
export { Layout, refocusLayout } from './Layout'; export { Layout } from './Layout';
export { NtosWindow } from './NtosWindow'; export { NtosWindow } from './NtosWindow';
export { Pane } from './Pane';
export { Window } from './Window'; export { Window } from './Window';