mirror of
https://github.com/Aurorastation/Aurora.3.git
synced 2026-08-22 12:29:46 +01:00
No player-facing changes. Ports several PRs from tgstation, including: https://github.com/tgstation/tgstation/pull/66862 https://github.com/tgstation/tgstation/pull/80189 https://github.com/tgstation/tgstation/pull/90317 https://github.com/tgstation/tgstation/pull/91379 In addition, removes IntelliCode from the recommended extensions. This is deprecated, and we prefer not to force the replacement onto contributors. Contributors may individually choose to use this instead.
452 lines
12 KiB
JavaScript
452 lines
12 KiB
JavaScript
/**
|
|
* @file
|
|
* @copyright 2020 Aleksej Komarov
|
|
* @license MIT
|
|
*/
|
|
|
|
import { toFixed } from 'common/math';
|
|
import { useLocalState } from 'tgui/backend';
|
|
import { useDispatch, useSelector } from 'common/redux';
|
|
import {
|
|
Box,
|
|
Button,
|
|
ColorBox,
|
|
Divider,
|
|
Dropdown,
|
|
Flex,
|
|
Input,
|
|
LabeledList,
|
|
NumberInput,
|
|
Section,
|
|
Stack,
|
|
Tabs,
|
|
TextArea,
|
|
} from 'tgui/components';
|
|
import { ChatPageSettings } from '../chat';
|
|
import {
|
|
rebuildChat,
|
|
saveChatToDisk,
|
|
clearChatMessages,
|
|
} from '../chat/actions';
|
|
import { THEMES } from '../themes';
|
|
import {
|
|
changeSettingsTab,
|
|
updateSettings,
|
|
addHighlightSetting,
|
|
removeHighlightSetting,
|
|
updateHighlightSetting,
|
|
} from './actions';
|
|
import { SETTINGS_TABS, FONTS, MAX_HIGHLIGHT_SETTINGS } from './constants';
|
|
import { resetPaneSplitters, setEditPaneSplitters } from './scaling';
|
|
import {
|
|
selectActiveTab,
|
|
selectSettings,
|
|
selectHighlightSettings,
|
|
selectHighlightSettingById,
|
|
} from './selectors';
|
|
import { IMPL_IFRAME_INDEXED_DB, storage } from 'common/storage';
|
|
|
|
export const SettingsPanel = (props, context) => {
|
|
const activeTab = useSelector(context, selectActiveTab);
|
|
const dispatch = useDispatch(context);
|
|
return (
|
|
<Stack fill>
|
|
<Stack.Item>
|
|
<Section fitted fill minHeight="8em">
|
|
<Tabs vertical>
|
|
{SETTINGS_TABS.map((tab) => (
|
|
<Tabs.Tab
|
|
key={tab.id}
|
|
selected={tab.id === activeTab}
|
|
onClick={() =>
|
|
dispatch(
|
|
changeSettingsTab({
|
|
tabId: tab.id,
|
|
}),
|
|
)
|
|
}
|
|
>
|
|
{tab.name}
|
|
</Tabs.Tab>
|
|
))}
|
|
</Tabs>
|
|
</Section>
|
|
</Stack.Item>
|
|
<Stack.Item grow={1} basis={0}>
|
|
{activeTab === 'general' && <SettingsGeneral />}
|
|
{activeTab === 'chatPage' && <ChatPageSettings />}
|
|
{activeTab === 'textHighlight' && <TextHighlightSettings />}
|
|
</Stack.Item>
|
|
</Stack>
|
|
);
|
|
};
|
|
|
|
export const SettingsGeneral = (props, context) => {
|
|
const { theme, fontFamily, fontSize, lineHeight, maxMessages } = useSelector(
|
|
context,
|
|
selectSettings,
|
|
);
|
|
const dispatch = useDispatch(context);
|
|
const [freeFont, setFreeFont] = useLocalState(context, 'freeFont', false);
|
|
const [editingPanes, setEditingPanes] = useLocalState(
|
|
context,
|
|
'uiScaling',
|
|
false,
|
|
);
|
|
return (
|
|
<Section>
|
|
<LabeledList>
|
|
<LabeledList.Item label="Theme">
|
|
<Dropdown
|
|
selected={theme}
|
|
options={THEMES}
|
|
onSelected={(value) =>
|
|
dispatch(
|
|
updateSettings({
|
|
theme: value,
|
|
}),
|
|
)
|
|
}
|
|
/>
|
|
</LabeledList.Item>
|
|
<LabeledList.Item label="UI sizes">
|
|
<Stack>
|
|
<Stack.Item>
|
|
<Button
|
|
onClick={() =>
|
|
setEditingPanes((val) => {
|
|
setEditPaneSplitters(!val);
|
|
return !val;
|
|
})
|
|
}
|
|
color={editingPanes ? 'red' : undefined}
|
|
icon={editingPanes ? 'save' : undefined}
|
|
>
|
|
{editingPanes ? 'Save' : 'Adjust UI Sizes'}
|
|
</Button>
|
|
</Stack.Item>
|
|
<Stack.Item>
|
|
<Button onClick={resetPaneSplitters} icon="refresh" color="red">
|
|
Reset
|
|
</Button>
|
|
</Stack.Item>
|
|
</Stack>
|
|
</LabeledList.Item>
|
|
<LabeledList.Item label="Font style">
|
|
<Stack inline align="baseline">
|
|
<Stack.Item>
|
|
{(!freeFont && (
|
|
<Dropdown
|
|
selected={fontFamily}
|
|
options={FONTS}
|
|
onSelected={(value) =>
|
|
dispatch(
|
|
updateSettings({
|
|
fontFamily: value,
|
|
}),
|
|
)
|
|
}
|
|
/>
|
|
)) || (
|
|
<Input
|
|
value={fontFamily}
|
|
onChange={(e, value) =>
|
|
dispatch(
|
|
updateSettings({
|
|
fontFamily: value,
|
|
}),
|
|
)
|
|
}
|
|
/>
|
|
)}
|
|
</Stack.Item>
|
|
<Stack.Item>
|
|
<Button
|
|
content="Custom font"
|
|
icon={freeFont ? 'lock-open' : 'lock'}
|
|
color={freeFont ? 'good' : 'bad'}
|
|
ml={1}
|
|
onClick={() => {
|
|
setFreeFont(!freeFont);
|
|
}}
|
|
/>
|
|
</Stack.Item>
|
|
</Stack>
|
|
</LabeledList.Item>
|
|
<LabeledList.Item label="Font size">
|
|
<NumberInput
|
|
width="4em"
|
|
step={1}
|
|
stepPixelSize={10}
|
|
minValue={8}
|
|
maxValue={32}
|
|
value={fontSize}
|
|
unit="px"
|
|
format={(value) => toFixed(value)}
|
|
onChange={(e, value) =>
|
|
dispatch(
|
|
updateSettings({
|
|
fontSize: value,
|
|
}),
|
|
)
|
|
}
|
|
/>
|
|
</LabeledList.Item>
|
|
<LabeledList.Item label="Line height">
|
|
<NumberInput
|
|
width="4em"
|
|
step={0.01}
|
|
stepPixelSize={2}
|
|
minValue={0.8}
|
|
maxValue={5}
|
|
value={lineHeight}
|
|
format={(value) => toFixed(value, 2)}
|
|
onDrag={(e, value) =>
|
|
dispatch(
|
|
updateSettings({
|
|
lineHeight: value,
|
|
}),
|
|
)
|
|
}
|
|
/>
|
|
</LabeledList.Item>
|
|
<LabeledList.Item label="Max messages">
|
|
<NumberInput
|
|
width="4em"
|
|
step={50}
|
|
stepPixelSize={2}
|
|
minValue={2000}
|
|
maxValue={32000}
|
|
value={maxMessages}
|
|
format={(value) => toFixed(value)}
|
|
onChange={(e, value) => {
|
|
storage.backendPromise.then((promise) => {
|
|
if (promise.impl === IMPL_IFRAME_INDEXED_DB) {
|
|
promise.setNumberStored(value);
|
|
}
|
|
});
|
|
|
|
dispatch(
|
|
updateSettings({
|
|
maxMessages: value,
|
|
}),
|
|
);
|
|
}}
|
|
/>
|
|
</LabeledList.Item>
|
|
</LabeledList>
|
|
<Divider />
|
|
<Button icon="eraser" onClick={() => dispatch(clearChatMessages())}>
|
|
Clear chat log
|
|
</Button>
|
|
<Button icon="save" onClick={() => dispatch(saveChatToDisk())}>
|
|
Save chat log
|
|
</Button>
|
|
</Section>
|
|
);
|
|
};
|
|
|
|
const TextHighlightSettings = (props, context) => {
|
|
const highlightSettings = useSelector(context, selectHighlightSettings);
|
|
const settings = useSelector(context, selectSettings);
|
|
const dispatch = useDispatch(context);
|
|
return (
|
|
<Section fill scrollable height="200px">
|
|
<Section p={0}>
|
|
<Flex direction="column">
|
|
{highlightSettings.map((id, i) => (
|
|
<TextHighlightSetting
|
|
key={i}
|
|
id={id}
|
|
mb={i + 1 === highlightSettings.length ? 0 : '10px'}
|
|
/>
|
|
))}
|
|
{highlightSettings.length < MAX_HIGHLIGHT_SETTINGS && (
|
|
<Flex.Item>
|
|
<Button
|
|
color="transparent"
|
|
icon="plus"
|
|
content="Add Highlight Setting"
|
|
onClick={() => {
|
|
dispatch(addHighlightSetting());
|
|
}}
|
|
/>
|
|
</Flex.Item>
|
|
)}
|
|
</Flex>
|
|
</Section>
|
|
<Divider />
|
|
<Box>
|
|
<Button
|
|
icon="check"
|
|
onClick={() => dispatch(rebuildChat(settings.maxMessages))}
|
|
>
|
|
Apply now
|
|
</Button>
|
|
<Box inline fontSize="0.9em" ml={1} color="label">
|
|
Can freeze the chat for a while.
|
|
</Box>
|
|
</Box>
|
|
</Section>
|
|
);
|
|
};
|
|
|
|
const TextHighlightSetting = (props, context) => {
|
|
const { id, ...rest } = props;
|
|
const highlightSettingById = useSelector(context, selectHighlightSettingById);
|
|
const dispatch = useDispatch(context);
|
|
const {
|
|
highlightColor,
|
|
highlightText,
|
|
highlightWholeMessage,
|
|
backgroundHighlightColor,
|
|
backgroundHighlightOpacity,
|
|
matchWord,
|
|
matchCase,
|
|
} = highlightSettingById[id];
|
|
return (
|
|
<Flex.Item {...rest}>
|
|
<Flex mb={1} color="label" align="baseline">
|
|
<Flex.Item grow>
|
|
<Button
|
|
content="Delete"
|
|
color="transparent"
|
|
icon="times"
|
|
onClick={() =>
|
|
dispatch(
|
|
removeHighlightSetting({
|
|
id: id,
|
|
}),
|
|
)
|
|
}
|
|
/>
|
|
</Flex.Item>
|
|
<Flex.Item>
|
|
{highlightWholeMessage && (
|
|
<>
|
|
<label>Opacity: </label>
|
|
<NumberInput
|
|
width="4em"
|
|
step={1}
|
|
stepPixelSize={5}
|
|
minValue={1}
|
|
maxValue={100}
|
|
unit="%"
|
|
value={backgroundHighlightOpacity}
|
|
format={(value) => toFixed(value)}
|
|
onDrag={(e, value) =>
|
|
dispatch(
|
|
updateHighlightSetting({
|
|
id: id,
|
|
backgroundHighlightOpacity: value,
|
|
}),
|
|
)
|
|
}
|
|
/>
|
|
</>
|
|
)}
|
|
</Flex.Item>
|
|
<Flex.Item shrink={0}>
|
|
{highlightWholeMessage && (
|
|
<>
|
|
<ColorBox mr={1} color={backgroundHighlightColor} />
|
|
<Input
|
|
mr={1}
|
|
width="5em"
|
|
monospace
|
|
placeholder="#ffdd44"
|
|
value={backgroundHighlightColor}
|
|
onInput={(e, value) =>
|
|
dispatch(
|
|
updateHighlightSetting({
|
|
id: id,
|
|
backgroundHighlightColor: value,
|
|
}),
|
|
)
|
|
}
|
|
/>
|
|
</>
|
|
)}
|
|
</Flex.Item>
|
|
<Flex.Item>
|
|
<Button.Checkbox
|
|
checked={highlightWholeMessage}
|
|
content="Whole Message"
|
|
tooltip="If this option is selected, the entire message will be highlighted in the color defined to the left."
|
|
mr="5px"
|
|
onClick={() =>
|
|
dispatch(
|
|
updateHighlightSetting({
|
|
id: id,
|
|
highlightWholeMessage: !highlightWholeMessage,
|
|
}),
|
|
)
|
|
}
|
|
/>
|
|
</Flex.Item>
|
|
<Flex.Item>
|
|
<Button.Checkbox
|
|
content="Exact"
|
|
checked={matchWord}
|
|
tooltipPosition="bottom-start"
|
|
tooltip="If this option is selected, only exact matches (no extra letters before or after) will trigger. Not compatible with punctuation. Overriden if regex is used."
|
|
onClick={() =>
|
|
dispatch(
|
|
updateHighlightSetting({
|
|
id: id,
|
|
matchWord: !matchWord,
|
|
}),
|
|
)
|
|
}
|
|
/>
|
|
</Flex.Item>
|
|
<Flex.Item>
|
|
<Button.Checkbox
|
|
content="Case"
|
|
tooltip="If this option is selected, the highlight will be case-sensitive."
|
|
checked={matchCase}
|
|
onClick={() =>
|
|
dispatch(
|
|
updateHighlightSetting({
|
|
id: id,
|
|
matchCase: !matchCase,
|
|
}),
|
|
)
|
|
}
|
|
/>
|
|
</Flex.Item>
|
|
<Flex.Item shrink={0}>
|
|
<ColorBox mr={1} color={highlightColor} />
|
|
<Input
|
|
width="5em"
|
|
monospace
|
|
placeholder="#ffffff"
|
|
value={highlightColor}
|
|
onInput={(e, value) =>
|
|
dispatch(
|
|
updateHighlightSetting({
|
|
id: id,
|
|
highlightColor: value,
|
|
}),
|
|
)
|
|
}
|
|
/>
|
|
</Flex.Item>
|
|
</Flex>
|
|
<TextArea
|
|
height="3em"
|
|
value={highlightText}
|
|
placeholder="Put words to highlight here. Separate terms with commas, i.e. (term1, term2, term3)"
|
|
onChange={(e, value) =>
|
|
dispatch(
|
|
updateHighlightSetting({
|
|
id: id,
|
|
highlightText: value,
|
|
}),
|
|
)
|
|
}
|
|
/>
|
|
</Flex.Item>
|
|
);
|
|
};
|