diff --git a/app/src/App.tsx b/app/src/App.tsx index dba635fe..7a6a0fd1 100644 --- a/app/src/App.tsx +++ b/app/src/App.tsx @@ -4,6 +4,7 @@ import { type Connect, createLink } from "./features/onboarding/link"; import { Onboarding } from "./features/onboarding/Onboarding"; import type { Host } from "./lib/host"; import { useCloseBehavior } from "./shell/closing"; +import { useMotionRoot } from "./shell/motionRoot"; import { TitleBar } from "./shell/TitleBar"; import { useThemeRoot } from "./shell/theme"; import { Workspace } from "./shell/Workspace"; @@ -13,6 +14,7 @@ import { Toaster } from "./ui/toast"; export function App({ host, connect }: { host: Host; connect: Connect }) { useThemeRoot(); + useMotionRoot(); useZoomRoot(host); useCloseBehavior(host); const [link] = useState(() => createLink(host, connect)); diff --git a/app/src/features/account/AppearanceSettings.tsx b/app/src/features/account/AppearanceSettings.tsx new file mode 100644 index 00000000..6c68bccf --- /dev/null +++ b/app/src/features/account/AppearanceSettings.tsx @@ -0,0 +1,42 @@ +// Settings, "Appearance": theme, size and motion (spec 15.3). + +import { useT } from "../../i18n"; +import { prefs, usePref } from "../../shell/prefs"; +import { setTheme, type ThemeChoice, useTheme } from "../../shell/theme"; +import { DEFAULT_ZOOM, setZoom, useZoom, ZOOM_LEVELS, type ZoomLevel } from "../../shell/zoom"; +import { Choices } from "../../ui/Choices"; +import { Field, Section, Toggle } from "./settingsParts"; + +export function AppearanceSettings() { + const t = useT(); + const s = t.account.settings; + const { choice: theme } = useTheme(); + const zoom = useZoom(); + const lessMotion = usePref(prefs.lessMotion); + const themes: { value: ThemeChoice; label: string }[] = [ + { value: "system", label: s.themes.system }, + { value: "light", label: s.themes.light }, + { value: "dark", label: s.themes.dark }, + ]; + const sizes = ZOOM_LEVELS.map((level) => ({ + value: level, + label: t.shell.zoom.level(Math.round(level * 100), level === DEFAULT_ZOOM), + })); + + return ( +
+ + + + + label={s.size} value={zoom} options={sizes} onChange={setZoom} /> + + +
+ ); +} diff --git a/app/src/features/account/BackgroundSettings.tsx b/app/src/features/account/BackgroundSettings.tsx deleted file mode 100644 index a422e603..00000000 --- a/app/src/features/account/BackgroundSettings.tsx +++ /dev/null @@ -1,40 +0,0 @@ -// Settings, "In the background" (spec 15.1): whether the bots keep working -// after the window closes, whether Botloft starts with Windows, and -// whether the computer stays awake while they work. - -import { useT } from "../../i18n"; -import { setWhenClosed, useWhenClosed } from "../../shell/closing"; -import { Section, Toggle } from "./settingsParts"; -import { useDaemonSettings } from "./useDaemonSettings"; - -export function BackgroundSettings() { - const s = useT().account.settings; - const whenClosed = useWhenClosed(); - const { settings, change } = useDaemonSettings(); - const startWithWindows = settings?.startWithWindows ?? true; - - return ( -
- setWhenClosed(on ? "keep" : "stop")} - /> - change({ startWithWindows: on })} - /> - change({ keepAwake: on })} - /> -
- ); -} diff --git a/app/src/features/account/ChatSettings.tsx b/app/src/features/account/ChatSettings.tsx new file mode 100644 index 00000000..5c5ec162 --- /dev/null +++ b/app/src/features/account/ChatSettings.tsx @@ -0,0 +1,28 @@ +// Settings, "Chat": how a message is sent, and whether the panel beside +// the chat follows what the bot starts doing (spec 15.1). + +import { useT } from "../../i18n"; +import { prefs, usePref } from "../../shell/prefs"; +import { Section, Toggle } from "./settingsParts"; + +export function ChatSettings() { + const s = useT().account.settings; + const enterSends = usePref(prefs.enterSends); + const followBot = usePref(prefs.followBot); + return ( +
+ + +
+ ); +} diff --git a/app/src/features/account/GeneralSettings.tsx b/app/src/features/account/GeneralSettings.tsx new file mode 100644 index 00000000..327afbff --- /dev/null +++ b/app/src/features/account/GeneralSettings.tsx @@ -0,0 +1,59 @@ +// Settings, "General": the language, and what Botloft does in the +// background (spec 15.1). + +import { + LOCALES, + type LocaleChoice, + setLocaleChoice, + systemLocale, + useLocale, + useT, +} from "../../i18n"; +import { prefs, usePref } from "../../shell/prefs"; +import { Select } from "../../ui/Select"; +import { Section, Toggle } from "./settingsParts"; +import { useDaemonSettings } from "./useDaemonSettings"; + +export function GeneralSettings() { + const t = useT(); + const s = t.account.settings; + const whenClosed = usePref(prefs.whenClosed); + const { settings, change } = useDaemonSettings(); + const startWithWindows = settings?.startWithWindows ?? true; + const { choice: locale } = useLocale(); + const systemName = LOCALES.find((entry) => entry.id === systemLocale())?.name ?? "English"; + const languages: { value: LocaleChoice; label: string }[] = [ + { value: "system", label: t.shell.language.system(systemName) }, + ...LOCALES.map((entry) => ({ value: entry.id as LocaleChoice, label: entry.name })), + ]; + + return ( + <> +
+