/** * @file AppearanceSettings.tsx * @description Settings → Appearance (`/settings/appearance`): the per-user * Chat UI default (`ChatUiSettings`, `#chat-ui`), whether the session view's * tabs hold every workspace's sessions (`lib/workspaceTabs.ts`, * `#workspace-tabs`), the interface language, the * terminal font (`lib/terminalFont.ts`, with a preview line), the * Dark / Light / System mode switch and one named-theme picker per mode, and the * corner pet (`PetSettings`, deep-linked as `#pet`) — the old General and Pet * pages redirect here. Theme state is in `hooks/useTheme.ts`, palettes in * `index.css`. Each card's swatch re-scopes the theme's CSS variables on its * own element (`dark` class or `data-mode`, plus `data-theme`), so the preview * is painted by the same tokens as the app — no hex is duplicated here. * **Reset to defaults** (after a confirm) returns to Dark with both stock themes. * **Run setup again** reopens the first-run wizard (`components/onboarding`). * @author Nguyễn Ngọc Trí Vĩ */ import { useState } from "react"; import { useTranslation } from "react-i18next"; import { Monitor, Moon, RotateCcw, Sun } from "lucide-react"; import { SettingsRow, SettingsSection, SettingsSegmented, SettingsSwitchRow } from "./SettingsRow"; import { PetSettings } from "./PetSettings"; import { ChatUiSettings } from "./ChatUiSettings"; import { Select } from "../Select"; import { THEMES, useTheme, type Theme, type ThemeMode } from "../../hooks/useTheme"; import { confirmDialog } from "../../lib/confirm"; import { resetOnboarding } from "../../lib/userPrefs"; import { toast } from "../../lib/toast"; import { TERMINAL_FONTS, readTerminalFont, writeTerminalFont, type TerminalFont, } from "../../lib/terminalFont"; import { readAllTabs, writeAllTabs } from "../../lib/workspaceTabs"; export const THEME_LABELS: Record = { darcula: "Darcula", "apple-dark": "Apple Dark", "apple-light": "Apple Light", canvas: "Canvas", "orca-dark": "Orca Dark", "orca-light": "Orca Light", }; const MODES: { mode: ThemeMode; Icon: typeof Moon }[] = [ { mode: "dark", Icon: Moon }, { mode: "light", Icon: Sun }, { mode: "system", Icon: Monitor }, ]; export function Swatch({ mode, name }: { mode: Theme; name: string }) { return (
const{" "} x{" "} ={" "} "ok"
); } export function AppearanceSettings() { const { t, i18n } = useTranslation("settings"); const { mode, theme, themeFor, setMode, setThemeFor } = useTheme(); const [termFont, setTermFont] = useState(readTerminalFont); const [allTabs, setAllTabs] = useState(readAllTabs); const pickTermFont = (font: TerminalFont) => { writeTerminalFont(font); setTermFont(font); }; const reset = async () => { const ok = await confirmDialog({ title: t("appearance.resetTitle"), message: t("appearance.resetBody"), confirmLabel: t("appearance.reset"), }); if (!ok) return; setMode("dark"); setThemeFor("dark", "default"); setThemeFor("light", "default"); toast.success(t("appearance.resetDone")); }; return (
{ writeAllTabs(on); setAllTabs(on); }} /> void i18n.changeLanguage(v)} options={[ { value: "en" as const, label: t("language.names.en") }, { value: "vi" as const, label: t("language.names.vi") }, ]} /> } /> label={t("terminalFont.title")} value={termFont} onChange={pickTermFont} options={(Object.keys(TERMINAL_FONTS) as TerminalFont[]).map((f) => ({ value: f, label: t(`terminalFont.names.${f}`), }))} /> } />

❯ Tiếng Việt: ấ ế ố ồ ủ ữ — const x = {"{ ok: true }"}; 0O il1|

{t("appearance.theme")}

({ value: m, label: ( <> {t(`appearance.modes.${m}`)} ), }))} /> } /> {(["dark", "light"] as const).map((group) => (

{t(`appearance.${group}Themes`)} {theme === group && ( {t("appearance.active")} )}

{THEMES[group].map((name) => { const selected = themeFor[group] === name; const label = THEME_LABELS[name] ?? t("appearance.default"); return ( ); })}
))}

{t("appearance.pet")}

{t("appearance.petDescription")}

{t("onboarding:rerun.button")} } />
); }