/** * Welcome › Customize (IntelliJ's quick settings): colour theme, accent, IDE * font size, keymap and startup behaviour, written straight to the global * settings keys the Settings dialog edits. */ import { useMemo, useState, type CSSProperties, type ReactNode } from "react"; import { executeAction } from "@/platform/actions"; import { call, commands, isTauri } from "@/platform/ipc"; import { defaultPreset, PRESET_TITLES } from "@/platform/keymap"; import { useProject } from "@/platform/project"; import { Checkbox, Select, type SelectOption, T } from "@/ui"; import { notifications } from "@/workbench/contributions"; import { ACCENT_PRESETS, DEFAULT_ACCENT, themeSelectOptions, useThemeChoices } from "@/workbench/theme"; import { reopenEnabled, setReopenPreference, useSession } from "./session"; import styles from "./Welcome.module.css"; import { isThemeChoiceId, type ThemeChoiceId } from "@/platform/themes"; import { tx } from "@/platform/i18n"; type Theme = ThemeChoiceId; const DEFAULT_UI_FONT_SIZE = 13; const DEFAULT_UI_FONT = "Inter"; export const UI_FONT_SIZES = [10, 11, 12, 13, 14, 15, 16, 17, 18]; /** Writes one global settings key and publishes the new effective settings. */ export async function setGlobalSetting(key: string, value: unknown): Promise { if (!isTauri()) return; try { useProject.setState({ settings: await call(commands.settingsSet("global", key, JSON.stringify(value))) }); } catch (e) { notifications().notify({ level: "error", title: "Cannot save setting", body: e instanceof Error ? e.message : String(e), key: `welcome.setting:${key}`, }); } } interface AppearanceView { theme: Theme; syncWithOs: boolean; accent: string; fontSize: number; keymap: string; } /** The values Customize shows, from (possibly partial / legacy) settings. */ export function customizeView(settings: ReturnType["settings"]): AppearanceView { const a = (settings?.appearance ?? {}) as Record; const theme = isThemeChoiceId(a.theme) ? a.theme : "light"; const custom = a.useCustomFont === true; const size = typeof a.uiFontSize === "number" ? a.uiFontSize : DEFAULT_UI_FONT_SIZE; return { theme, syncWithOs: a.syncWithOs === true || a.theme === "system", accent: typeof a.accent === "string" ? a.accent.toLowerCase() : DEFAULT_ACCENT, fontSize: custom ? size : DEFAULT_UI_FONT_SIZE, keymap: typeof settings?.keymap === "string" && settings.keymap ? settings.keymap : defaultPreset(), }; } function Field({ label, children }: { label: string; children: ReactNode }) { return (
{label}
{children}
); } function Section({ title, children }: { title: string; children: ReactNode }) { return (

{title}

{children}
); } export function CustomizePage() { const settings = useProject((s) => s.settings); const pref = useSession((s) => s.reopenPref); const v = customizeView(settings); // Optimistic value while a write is in flight (and the only value outside Tauri). const [pending, setPending] = useState>({}); const shown = { ...v, reopen: reopenEnabled(settings, pref), ...pending }; const update = (field: K, value: (typeof shown)[K], write: () => Promise) => { setPending((p) => ({ ...p, [field]: value })); void write().finally(() => setPending((p) => { const { [field]: _, ...rest } = p; return rest; }), ); }; const themes = useThemeChoices(); const keymaps = useMemo(() => { const custom = (settings?.customKeymaps ?? {}) as Record; const list: SelectOption[] = Object.entries(PRESET_TITLES).map(([value, label]) => ({ value, label })); for (const [id, k] of Object.entries(custom)) list.push({ value: id, label: k?.name || id }); if (!list.some((o) => o.value === shown.keymap)) list.push({ value: shown.keymap, label: shown.keymap }); return list; }, [settings?.customKeymaps, shown.keymap]); return (
value={shown.theme} options={themeSelectOptions(themes, shown.theme)} disabled={shown.syncWithOs} aria-label="Color theme" onChange={(t) => update("theme", t, () => setGlobalSetting("appearance.theme", t))} /> update("syncWithOs", on, async () => { await setGlobalSetting("appearance.syncWithOs", on); // Legacy "system" theme: make the explicit choice concrete. if (!on && settings?.appearance?.theme === "system") await setGlobalSetting("appearance.theme", "light"); }) } />
{ACCENT_PRESETS.map((p) => (
update("keymap", k, () => setGlobalSetting("keymap", k))} />
update("reopen", on, async () => { setReopenPreference(on); await setGlobalSetting("general.reopenLastProject", on); }) } />
); }