mirror of
https://github.com/stoatchat/for-legacy-web.git
synced 2026-03-07 09:25:27 +00:00
Use tabWidth 4 without actual tabs.
This commit is contained in:
@@ -13,15 +13,15 @@ import { connectState } from "../../../redux/connector";
|
||||
import { EmojiPacks, Settings } from "../../../redux/reducers/settings";
|
||||
|
||||
import {
|
||||
DEFAULT_FONT,
|
||||
DEFAULT_MONO_FONT,
|
||||
FONTS,
|
||||
FONT_KEYS,
|
||||
MONOSCAPE_FONTS,
|
||||
MONOSCAPE_FONT_KEYS,
|
||||
Theme,
|
||||
ThemeContext,
|
||||
ThemeOptions,
|
||||
DEFAULT_FONT,
|
||||
DEFAULT_MONO_FONT,
|
||||
FONTS,
|
||||
FONT_KEYS,
|
||||
MONOSCAPE_FONTS,
|
||||
MONOSCAPE_FONT_KEYS,
|
||||
Theme,
|
||||
ThemeContext,
|
||||
ThemeOptions,
|
||||
} from "../../../context/Theme";
|
||||
import { useIntermediate } from "../../../context/intermediate/Intermediate";
|
||||
|
||||
@@ -39,92 +39,92 @@ import openmojiSVG from "../assets/openmoji_emoji.svg";
|
||||
import twemojiSVG from "../assets/twemoji_emoji.svg";
|
||||
|
||||
interface Props {
|
||||
settings: Settings;
|
||||
settings: Settings;
|
||||
}
|
||||
|
||||
// ! FIXME: code needs to be rewritten to fix jittering
|
||||
export function Component(props: Props) {
|
||||
const theme = useContext(ThemeContext);
|
||||
const { writeClipboard, openScreen } = useIntermediate();
|
||||
const theme = useContext(ThemeContext);
|
||||
const { writeClipboard, openScreen } = useIntermediate();
|
||||
|
||||
function setTheme(theme: ThemeOptions) {
|
||||
dispatch({
|
||||
type: "SETTINGS_SET_THEME",
|
||||
theme,
|
||||
});
|
||||
}
|
||||
function setTheme(theme: ThemeOptions) {
|
||||
dispatch({
|
||||
type: "SETTINGS_SET_THEME",
|
||||
theme,
|
||||
});
|
||||
}
|
||||
|
||||
function pushOverride(custom: Partial<Theme>) {
|
||||
dispatch({
|
||||
type: "SETTINGS_SET_THEME_OVERRIDE",
|
||||
custom,
|
||||
});
|
||||
}
|
||||
function pushOverride(custom: Partial<Theme>) {
|
||||
dispatch({
|
||||
type: "SETTINGS_SET_THEME_OVERRIDE",
|
||||
custom,
|
||||
});
|
||||
}
|
||||
|
||||
function setAccent(accent: string) {
|
||||
setOverride({
|
||||
accent,
|
||||
"scrollbar-thumb": pSBC(-0.2, accent),
|
||||
});
|
||||
}
|
||||
function setAccent(accent: string) {
|
||||
setOverride({
|
||||
accent,
|
||||
"scrollbar-thumb": pSBC(-0.2, accent),
|
||||
});
|
||||
}
|
||||
|
||||
const emojiPack = props.settings.appearance?.emojiPack ?? "mutant";
|
||||
function setEmojiPack(emojiPack: EmojiPacks) {
|
||||
dispatch({
|
||||
type: "SETTINGS_SET_APPEARANCE",
|
||||
options: {
|
||||
emojiPack,
|
||||
},
|
||||
});
|
||||
}
|
||||
const emojiPack = props.settings.appearance?.emojiPack ?? "mutant";
|
||||
function setEmojiPack(emojiPack: EmojiPacks) {
|
||||
dispatch({
|
||||
type: "SETTINGS_SET_APPEARANCE",
|
||||
options: {
|
||||
emojiPack,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const setOverride = useCallback(debounce(pushOverride, 200), []) as (
|
||||
custom: Partial<Theme>,
|
||||
) => void;
|
||||
const [css, setCSS] = useState(props.settings.theme?.custom?.css ?? "");
|
||||
const setOverride = useCallback(debounce(pushOverride, 200), []) as (
|
||||
custom: Partial<Theme>,
|
||||
) => void;
|
||||
const [css, setCSS] = useState(props.settings.theme?.custom?.css ?? "");
|
||||
|
||||
useEffect(() => setOverride({ css }), [css]);
|
||||
useEffect(() => setOverride({ css }), [css]);
|
||||
|
||||
const selected = props.settings.theme?.preset ?? "dark";
|
||||
return (
|
||||
<div className={styles.appearance}>
|
||||
<h3>
|
||||
<Text id="app.settings.pages.appearance.theme" />
|
||||
</h3>
|
||||
<div className={styles.themes}>
|
||||
<div className={styles.theme}>
|
||||
<img
|
||||
src={lightSVG}
|
||||
data-active={selected === "light"}
|
||||
onClick={() =>
|
||||
selected !== "light" &&
|
||||
setTheme({ preset: "light" })
|
||||
}
|
||||
/>
|
||||
<h4>
|
||||
<Text id="app.settings.pages.appearance.color.light" />
|
||||
</h4>
|
||||
</div>
|
||||
<div className={styles.theme}>
|
||||
<img
|
||||
src={darkSVG}
|
||||
data-active={selected === "dark"}
|
||||
onClick={() =>
|
||||
selected !== "dark" && setTheme({ preset: "dark" })
|
||||
}
|
||||
/>
|
||||
<h4>
|
||||
<Text id="app.settings.pages.appearance.color.dark" />
|
||||
</h4>
|
||||
</div>
|
||||
</div>
|
||||
const selected = props.settings.theme?.preset ?? "dark";
|
||||
return (
|
||||
<div className={styles.appearance}>
|
||||
<h3>
|
||||
<Text id="app.settings.pages.appearance.theme" />
|
||||
</h3>
|
||||
<div className={styles.themes}>
|
||||
<div className={styles.theme}>
|
||||
<img
|
||||
src={lightSVG}
|
||||
data-active={selected === "light"}
|
||||
onClick={() =>
|
||||
selected !== "light" &&
|
||||
setTheme({ preset: "light" })
|
||||
}
|
||||
/>
|
||||
<h4>
|
||||
<Text id="app.settings.pages.appearance.color.light" />
|
||||
</h4>
|
||||
</div>
|
||||
<div className={styles.theme}>
|
||||
<img
|
||||
src={darkSVG}
|
||||
data-active={selected === "dark"}
|
||||
onClick={() =>
|
||||
selected !== "dark" && setTheme({ preset: "dark" })
|
||||
}
|
||||
/>
|
||||
<h4>
|
||||
<Text id="app.settings.pages.appearance.color.dark" />
|
||||
</h4>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3>
|
||||
<Text id="app.settings.pages.appearance.accent_selector" />
|
||||
</h3>
|
||||
<ColourSwatches value={theme.accent} onChange={setAccent} />
|
||||
<h3>
|
||||
<Text id="app.settings.pages.appearance.accent_selector" />
|
||||
</h3>
|
||||
<ColourSwatches value={theme.accent} onChange={setAccent} />
|
||||
|
||||
{/*<h3>
|
||||
{/*<h3>
|
||||
<Text id="app.settings.pages.appearance.message_display" />
|
||||
</h3>
|
||||
<div className={styles.display}>
|
||||
@@ -146,229 +146,229 @@ export function Component(props: Props) {
|
||||
</Radio>
|
||||
</div>*/}
|
||||
|
||||
<h3>
|
||||
<Text id="app.settings.pages.appearance.font" />
|
||||
</h3>
|
||||
<ComboBox
|
||||
value={theme.font ?? DEFAULT_FONT}
|
||||
onChange={(e) =>
|
||||
setTheme({ custom: { font: e.currentTarget.value as any } })
|
||||
}>
|
||||
{FONT_KEYS.map((key) => (
|
||||
<option value={key}>
|
||||
{FONTS[key as keyof typeof FONTS].name}
|
||||
</option>
|
||||
))}
|
||||
</ComboBox>
|
||||
<p>
|
||||
<Checkbox
|
||||
checked={props.settings.theme?.ligatures === true}
|
||||
onChange={() =>
|
||||
setTheme({
|
||||
ligatures: !props.settings.theme?.ligatures,
|
||||
})
|
||||
}
|
||||
description={
|
||||
<Text id="app.settings.pages.appearance.ligatures_desc" />
|
||||
}>
|
||||
<Text id="app.settings.pages.appearance.ligatures" />
|
||||
</Checkbox>
|
||||
</p>
|
||||
<h3>
|
||||
<Text id="app.settings.pages.appearance.font" />
|
||||
</h3>
|
||||
<ComboBox
|
||||
value={theme.font ?? DEFAULT_FONT}
|
||||
onChange={(e) =>
|
||||
setTheme({ custom: { font: e.currentTarget.value as any } })
|
||||
}>
|
||||
{FONT_KEYS.map((key) => (
|
||||
<option value={key}>
|
||||
{FONTS[key as keyof typeof FONTS].name}
|
||||
</option>
|
||||
))}
|
||||
</ComboBox>
|
||||
<p>
|
||||
<Checkbox
|
||||
checked={props.settings.theme?.ligatures === true}
|
||||
onChange={() =>
|
||||
setTheme({
|
||||
ligatures: !props.settings.theme?.ligatures,
|
||||
})
|
||||
}
|
||||
description={
|
||||
<Text id="app.settings.pages.appearance.ligatures_desc" />
|
||||
}>
|
||||
<Text id="app.settings.pages.appearance.ligatures" />
|
||||
</Checkbox>
|
||||
</p>
|
||||
|
||||
<h3>
|
||||
<Text id="app.settings.pages.appearance.emoji_pack" />
|
||||
</h3>
|
||||
<div className={styles.emojiPack}>
|
||||
<div className={styles.row}>
|
||||
<div>
|
||||
<div
|
||||
className={styles.button}
|
||||
onClick={() => setEmojiPack("mutant")}
|
||||
data-active={emojiPack === "mutant"}>
|
||||
<img src={mutantSVG} draggable={false} />
|
||||
</div>
|
||||
<h4>
|
||||
Mutant Remix{" "}
|
||||
<a
|
||||
href="https://mutant.revolt.chat"
|
||||
target="_blank">
|
||||
(by Revolt)
|
||||
</a>
|
||||
</h4>
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
className={styles.button}
|
||||
onClick={() => setEmojiPack("twemoji")}
|
||||
data-active={emojiPack === "twemoji"}>
|
||||
<img src={twemojiSVG} draggable={false} />
|
||||
</div>
|
||||
<h4>Twemoji</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.row}>
|
||||
<div>
|
||||
<div
|
||||
className={styles.button}
|
||||
onClick={() => setEmojiPack("openmoji")}
|
||||
data-active={emojiPack === "openmoji"}>
|
||||
<img src={openmojiSVG} draggable={false} />
|
||||
</div>
|
||||
<h4>Openmoji</h4>
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
className={styles.button}
|
||||
onClick={() => setEmojiPack("noto")}
|
||||
data-active={emojiPack === "noto"}>
|
||||
<img src={notoSVG} draggable={false} />
|
||||
</div>
|
||||
<h4>Noto Emoji</h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<h3>
|
||||
<Text id="app.settings.pages.appearance.emoji_pack" />
|
||||
</h3>
|
||||
<div className={styles.emojiPack}>
|
||||
<div className={styles.row}>
|
||||
<div>
|
||||
<div
|
||||
className={styles.button}
|
||||
onClick={() => setEmojiPack("mutant")}
|
||||
data-active={emojiPack === "mutant"}>
|
||||
<img src={mutantSVG} draggable={false} />
|
||||
</div>
|
||||
<h4>
|
||||
Mutant Remix{" "}
|
||||
<a
|
||||
href="https://mutant.revolt.chat"
|
||||
target="_blank">
|
||||
(by Revolt)
|
||||
</a>
|
||||
</h4>
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
className={styles.button}
|
||||
onClick={() => setEmojiPack("twemoji")}
|
||||
data-active={emojiPack === "twemoji"}>
|
||||
<img src={twemojiSVG} draggable={false} />
|
||||
</div>
|
||||
<h4>Twemoji</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.row}>
|
||||
<div>
|
||||
<div
|
||||
className={styles.button}
|
||||
onClick={() => setEmojiPack("openmoji")}
|
||||
data-active={emojiPack === "openmoji"}>
|
||||
<img src={openmojiSVG} draggable={false} />
|
||||
</div>
|
||||
<h4>Openmoji</h4>
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
className={styles.button}
|
||||
onClick={() => setEmojiPack("noto")}
|
||||
data-active={emojiPack === "noto"}>
|
||||
<img src={notoSVG} draggable={false} />
|
||||
</div>
|
||||
<h4>Noto Emoji</h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<CollapsibleSection
|
||||
id="settings_advanced_appearance"
|
||||
defaultValue={false}
|
||||
summary={<Text id="app.settings.pages.appearance.advanced" />}>
|
||||
<h3>
|
||||
<Text id="app.settings.pages.appearance.overrides" />
|
||||
</h3>
|
||||
<div className={styles.actions}>
|
||||
<Button contrast onClick={() => setTheme({ custom: {} })}>
|
||||
<Text id="app.settings.pages.appearance.reset_overrides" />
|
||||
</Button>
|
||||
<Button
|
||||
contrast
|
||||
onClick={() => writeClipboard(JSON.stringify(theme))}>
|
||||
<Text id="app.settings.pages.appearance.export_clipboard" />
|
||||
</Button>
|
||||
<Button
|
||||
contrast
|
||||
onClick={async () => {
|
||||
const text = await navigator.clipboard.readText();
|
||||
setOverride(JSON.parse(text));
|
||||
}}>
|
||||
<Text id="app.settings.pages.appearance.import_clipboard" />
|
||||
</Button>
|
||||
<Button
|
||||
contrast
|
||||
onClick={async () => {
|
||||
openScreen({
|
||||
id: "_input",
|
||||
question: (
|
||||
<Text id="app.settings.pages.appearance.import_theme" />
|
||||
),
|
||||
field: (
|
||||
<Text id="app.settings.pages.appearance.theme_data" />
|
||||
),
|
||||
callback: async (string) =>
|
||||
setOverride(JSON.parse(string)),
|
||||
});
|
||||
}}>
|
||||
<Text id="app.settings.pages.appearance.import_manual" />
|
||||
</Button>
|
||||
</div>
|
||||
<div className={styles.overrides}>
|
||||
{(
|
||||
[
|
||||
"accent",
|
||||
"background",
|
||||
"foreground",
|
||||
"primary-background",
|
||||
"primary-header",
|
||||
"secondary-background",
|
||||
"secondary-foreground",
|
||||
"secondary-header",
|
||||
"tertiary-background",
|
||||
"tertiary-foreground",
|
||||
"block",
|
||||
"message-box",
|
||||
"mention",
|
||||
"scrollbar-thumb",
|
||||
"scrollbar-track",
|
||||
"status-online",
|
||||
"status-away",
|
||||
"status-busy",
|
||||
"status-streaming",
|
||||
"status-invisible",
|
||||
"success",
|
||||
"warning",
|
||||
"error",
|
||||
"hover",
|
||||
] as const
|
||||
).map((x) => (
|
||||
<div className={styles.entry} key={x}>
|
||||
<span>{x}</span>
|
||||
<div className={styles.override}>
|
||||
<div
|
||||
className={styles.picker}
|
||||
style={{ backgroundColor: theme[x] }}>
|
||||
<input
|
||||
type="color"
|
||||
value={theme[x]}
|
||||
onChange={(v) =>
|
||||
setOverride({
|
||||
[x]: v.currentTarget.value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<InputBox
|
||||
className={styles.text}
|
||||
value={theme[x]}
|
||||
onChange={(y) =>
|
||||
setOverride({
|
||||
[x]: y.currentTarget.value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<CollapsibleSection
|
||||
id="settings_advanced_appearance"
|
||||
defaultValue={false}
|
||||
summary={<Text id="app.settings.pages.appearance.advanced" />}>
|
||||
<h3>
|
||||
<Text id="app.settings.pages.appearance.overrides" />
|
||||
</h3>
|
||||
<div className={styles.actions}>
|
||||
<Button contrast onClick={() => setTheme({ custom: {} })}>
|
||||
<Text id="app.settings.pages.appearance.reset_overrides" />
|
||||
</Button>
|
||||
<Button
|
||||
contrast
|
||||
onClick={() => writeClipboard(JSON.stringify(theme))}>
|
||||
<Text id="app.settings.pages.appearance.export_clipboard" />
|
||||
</Button>
|
||||
<Button
|
||||
contrast
|
||||
onClick={async () => {
|
||||
const text = await navigator.clipboard.readText();
|
||||
setOverride(JSON.parse(text));
|
||||
}}>
|
||||
<Text id="app.settings.pages.appearance.import_clipboard" />
|
||||
</Button>
|
||||
<Button
|
||||
contrast
|
||||
onClick={async () => {
|
||||
openScreen({
|
||||
id: "_input",
|
||||
question: (
|
||||
<Text id="app.settings.pages.appearance.import_theme" />
|
||||
),
|
||||
field: (
|
||||
<Text id="app.settings.pages.appearance.theme_data" />
|
||||
),
|
||||
callback: async (string) =>
|
||||
setOverride(JSON.parse(string)),
|
||||
});
|
||||
}}>
|
||||
<Text id="app.settings.pages.appearance.import_manual" />
|
||||
</Button>
|
||||
</div>
|
||||
<div className={styles.overrides}>
|
||||
{(
|
||||
[
|
||||
"accent",
|
||||
"background",
|
||||
"foreground",
|
||||
"primary-background",
|
||||
"primary-header",
|
||||
"secondary-background",
|
||||
"secondary-foreground",
|
||||
"secondary-header",
|
||||
"tertiary-background",
|
||||
"tertiary-foreground",
|
||||
"block",
|
||||
"message-box",
|
||||
"mention",
|
||||
"scrollbar-thumb",
|
||||
"scrollbar-track",
|
||||
"status-online",
|
||||
"status-away",
|
||||
"status-busy",
|
||||
"status-streaming",
|
||||
"status-invisible",
|
||||
"success",
|
||||
"warning",
|
||||
"error",
|
||||
"hover",
|
||||
] as const
|
||||
).map((x) => (
|
||||
<div className={styles.entry} key={x}>
|
||||
<span>{x}</span>
|
||||
<div className={styles.override}>
|
||||
<div
|
||||
className={styles.picker}
|
||||
style={{ backgroundColor: theme[x] }}>
|
||||
<input
|
||||
type="color"
|
||||
value={theme[x]}
|
||||
onChange={(v) =>
|
||||
setOverride({
|
||||
[x]: v.currentTarget.value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<InputBox
|
||||
className={styles.text}
|
||||
value={theme[x]}
|
||||
onChange={(y) =>
|
||||
setOverride({
|
||||
[x]: y.currentTarget.value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<h3>
|
||||
<Text id="app.settings.pages.appearance.mono_font" />
|
||||
</h3>
|
||||
<ComboBox
|
||||
value={theme.monoscapeFont ?? DEFAULT_MONO_FONT}
|
||||
onChange={(e) =>
|
||||
setTheme({
|
||||
custom: {
|
||||
monoscapeFont: e.currentTarget.value as any,
|
||||
},
|
||||
})
|
||||
}>
|
||||
{MONOSCAPE_FONT_KEYS.map((key) => (
|
||||
<option value={key}>
|
||||
{
|
||||
MONOSCAPE_FONTS[
|
||||
key as keyof typeof MONOSCAPE_FONTS
|
||||
].name
|
||||
}
|
||||
</option>
|
||||
))}
|
||||
</ComboBox>
|
||||
<h3>
|
||||
<Text id="app.settings.pages.appearance.mono_font" />
|
||||
</h3>
|
||||
<ComboBox
|
||||
value={theme.monoscapeFont ?? DEFAULT_MONO_FONT}
|
||||
onChange={(e) =>
|
||||
setTheme({
|
||||
custom: {
|
||||
monoscapeFont: e.currentTarget.value as any,
|
||||
},
|
||||
})
|
||||
}>
|
||||
{MONOSCAPE_FONT_KEYS.map((key) => (
|
||||
<option value={key}>
|
||||
{
|
||||
MONOSCAPE_FONTS[
|
||||
key as keyof typeof MONOSCAPE_FONTS
|
||||
].name
|
||||
}
|
||||
</option>
|
||||
))}
|
||||
</ComboBox>
|
||||
|
||||
<h3>
|
||||
<Text id="app.settings.pages.appearance.custom_css" />
|
||||
</h3>
|
||||
<TextAreaAutoSize
|
||||
maxRows={20}
|
||||
minHeight={480}
|
||||
code
|
||||
value={css}
|
||||
onChange={(ev) => setCSS(ev.currentTarget.value)}
|
||||
/>
|
||||
</CollapsibleSection>
|
||||
</div>
|
||||
);
|
||||
<h3>
|
||||
<Text id="app.settings.pages.appearance.custom_css" />
|
||||
</h3>
|
||||
<TextAreaAutoSize
|
||||
maxRows={20}
|
||||
minHeight={480}
|
||||
code
|
||||
value={css}
|
||||
onChange={(ev) => setCSS(ev.currentTarget.value)}
|
||||
/>
|
||||
</CollapsibleSection>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export const Appearance = connectState(Component, (state) => {
|
||||
return {
|
||||
settings: state.settings,
|
||||
};
|
||||
return {
|
||||
settings: state.settings,
|
||||
};
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user