Skip to content

Commit

Permalink
Add a system color theme option
Browse files Browse the repository at this point in the history
  • Loading branch information
gingershaped committed Nov 6, 2024
1 parent 9a918d4 commit a2f1268
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 19 deletions.
4 changes: 2 additions & 2 deletions src/latest/scripts/ui/Editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { vscodeDark } from "@uiw/codemirror-theme-vscode";
import { vyxalCompletion } from "../language/sbcs/autocomplete";
import { autocompletion } from "@codemirror/autocomplete";
import { EditorView, highlightActiveLine, highlightActiveLineGutter, lineNumbers, showPanel } from "@codemirror/view";
import { Settings, Theme } from "./settings";
import { SettingsState, Theme } from "./settings";
import { githubLight } from "@uiw/codemirror-theme-github";
import { ElementDataContext } from "../interpreter/element-data";
import { createPortal } from "react-dom";
Expand Down Expand Up @@ -54,7 +54,7 @@ type EditorProps = {
utilWorker: UtilWorker,
code: string,
setCode: Dispatch<SetStateAction<string>>,
settings: Settings,
settings: SettingsState,
literate: boolean,
claimFocus: (state: ReactCodeMirrorRef) => unknown,
autoFocus?: boolean,
Expand Down
21 changes: 10 additions & 11 deletions src/latest/scripts/ui/Theseus.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { lazy, Suspense, useCallback, useContext, useEffect, useRef, useState }
import Header from "./Header";
import { Spinner, Tab, Nav, Button } from "react-bootstrap";
import { useImmer, useImmerReducer } from "use-immer";
import { isTheSeason, loadSettings, saveSettings, Settings, Theme } from "./settings";
import { isTheSeason, Theme, useSettings } from "./settings";
import { UtilWorker } from "../workers/util-api";
import { VyTerminalRef } from "./VyTerminal";
import { SettingsDialog } from "./dialogs/SettingsDialog";
Expand Down Expand Up @@ -48,12 +48,12 @@ export function Theseus({ permalink }: TheseusProps) {
const elementData = useContext(ElementDataContext)!;
const utilWorker = new UtilWorker(elementData.codepage);

const [settings, setSettings] = useImmer<Settings>(loadSettings());
const [settingsState, settings, setSettings] = useSettings();
const [timeout, setTimeout] = useState<number | null>(10);

const [flags, setFlags] = useImmer<Flags>(() => {
const initial = deserializeFlags(elementData.flagDefs, new Set(permalink?.flags ?? []));
if (permalink == null && settings.literateByDefault) {
if (permalink == null && settingsState.literateByDefault) {
initial.set(LITERATE_MODE_FLAG_NAME, true);
}
return initial;
Expand All @@ -79,15 +79,15 @@ export function Theseus({ permalink }: TheseusProps) {
const snowflakesRef = useRef<Snowflakes | null>(null);

useEffect(() => {
switch (settings.theme) {
switch (settingsState.theme) {
case Theme.Dark:
document.body.dataset["bsTheme"] = "dark";
break;
case Theme.Light:
document.body.dataset["bsTheme"] = "light";
break;
}
if (settings.snowing == "always" || (settings.snowing == "yes" && isTheSeason())) {
if (settingsState.snowing == "always" || (settingsState.snowing == "yes" && isTheSeason())) {
import(
/* webpackChunkName: "magic-snowflakes" */
"magic-snowflakes"
Expand All @@ -102,12 +102,11 @@ export function Theseus({ permalink }: TheseusProps) {
snowflakesRef.current?.stop();
snowflakesRef.current?.hide();
}
saveSettings(settings);
return () => {
snowflakesRef.current?.stop();
snowflakesRef.current?.hide();
};
}, [settings]);
}, [settingsState]);

useEffect(() => {
encodeHash({
Expand Down Expand Up @@ -168,7 +167,7 @@ export function Theseus({ permalink }: TheseusProps) {
<ElementOffcanvas
show={showElementOffcanvas}
setShow={setShowElementOffcanvas}
side={settings.elementsSide}
side={settingsState.elementsSide}
insertCharacter={(char) => {
const view = lastFocusedEditor?.view;
if (view != null) {
Expand Down Expand Up @@ -197,10 +196,10 @@ export function Theseus({ permalink }: TheseusProps) {
</div>
}
>
<Editor utilWorker={utilWorker} code={header} setCode={setHeader} settings={settings} literate={literate} claimFocus={setLastFocusedEditor} height="20cqh">
<Editor utilWorker={utilWorker} code={header} setCode={setHeader} settings={settingsState} literate={literate} claimFocus={setLastFocusedEditor} height="20cqh">
Header
</Editor>
<Editor utilWorker={utilWorker} code={code} setCode={setCode} settings={settings} literate={literate} claimFocus={setLastFocusedEditor} autoFocus height="60cqh">
<Editor utilWorker={utilWorker} code={code} setCode={setCode} settings={settingsState} literate={literate} claimFocus={setLastFocusedEditor} autoFocus height="60cqh">
<div className="d-flex align-items-center">
{bytecount}
{literate ? (
Expand All @@ -210,7 +209,7 @@ export function Theseus({ permalink }: TheseusProps) {
) : null}
</div>
</Editor>
<Editor utilWorker={utilWorker} code={footer} setCode={setFooter} settings={settings} literate={literate} claimFocus={setLastFocusedEditor} height="20cqh">
<Editor utilWorker={utilWorker} code={footer} setCode={setFooter} settings={settingsState} literate={literate} claimFocus={setLastFocusedEditor} height="20cqh">
Footer
</Editor>
</Suspense>
Expand Down
4 changes: 2 additions & 2 deletions src/latest/scripts/ui/dialogs/SettingsDialog.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ChangeEvent, Dispatch, ReactNode, SetStateAction, createContext, memo, useCallback, useContext, useEffect, useRef } from "react";
import { Button, FormCheck, FormLabel, FormText, Modal, ToggleButton, ToggleButtonGroup } from "react-bootstrap";
import { ElementsSide, Settings, Theme, isTheSeason } from "../settings";
import { ElementsSide, Settings, ThemeSetting, isTheSeason } from "../settings";
import type { Updater } from "use-immer";
import type { Draft } from "immer";
import FormRange from "react-bootstrap/esm/FormRange";
Expand Down Expand Up @@ -116,7 +116,7 @@ export const SettingsDialog = memo(function({ settings, setSettings, timeout, se
<SettingsContext.Provider value={{ settings, setSettings }}>
<SettingsToggleButtonGroup
name="theme"
enumType={Theme}
enumType={ThemeSetting}
property="theme"
>
Theme
Expand Down
47 changes: 43 additions & 4 deletions src/latest/scripts/ui/settings.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import { useEffect, useMemo, useState } from "react";
import { Updater, useImmer } from "use-immer";

export const Theme = {
Dark: "Dark",
Light: "Light",
Expand All @@ -8,16 +11,25 @@ export const ElementsSide = {
Right: "end",
} as const;

export type Settings = {
export type SettingsState = {
theme: keyof typeof Theme,
literateByDefault: boolean,
snowing: "yes" | "no" | "always",
highlightBrackets: "yes" | "yes-eof" | "no",
elementsSide: keyof typeof ElementsSide,
};

export const ThemeSetting = {
...Theme,
System: "System",
} as const;

export type Settings = Omit<SettingsState, "theme"> & {
theme: keyof typeof ThemeSetting,
};

const defaultSettings: Settings = {
theme: "Dark",
theme: "System",
literateByDefault: false,
snowing: "yes",
highlightBrackets: "yes",
Expand All @@ -39,7 +51,7 @@ export function isTheSeason() {
return false;
}

export function loadSettings(): Settings {
function loadSettings(): Settings {
let localSettings: Settings;
try {
localSettings = {
Expand All @@ -53,6 +65,33 @@ export function loadSettings(): Settings {
return localSettings;
}

export function saveSettings(settings: Settings) {
function saveSettings(settings: Settings) {
localStorage.setItem("settings", JSON.stringify(settings));
}

export function useSettings(): [SettingsState, Settings, Updater<Settings>] {
const prefersLightModeQuery = useMemo(() => window.matchMedia("(prefers-color-scheme: light)"), []);
const [settings, setSettings] = useImmer(() => loadSettings());
const [prefersLightMode, setPrefersLightMode] = useState(prefersLightModeQuery.matches);

useEffect(() => {
const listener = (event: MediaQueryListEvent) => {
setPrefersLightMode(event.matches);
};
prefersLightModeQuery.addEventListener("change", listener);
return () => {
prefersLightModeQuery.removeEventListener("change", listener);
};
}, [prefersLightModeQuery]);

useEffect(() => {
saveSettings(settings);
}, [settings]);

const settingsState: SettingsState = {
...settings,
theme: settings.theme == "System" ? (prefersLightMode ? "Light" : "Dark") : settings.theme,
};

return [settingsState, settings, setSettings];
}

0 comments on commit a2f1268

Please sign in to comment.