import { useEffect, useRef, useState } from "react"; import { SegmentedControl } from "@mantine/core"; import { IconCheck, IconPalette } from "@tabler/icons-react"; import { VISUAL_THEMES, themeVariables, type Appearance } from "./themes"; export function AppearancePicker({ value, onChange, visualThemeId = "chatgpt", appearance = "dark", onVisualThemeChange }: { value: "light" | "dark" | "system"; onChange: (value: string) => void; visualThemeId?: string; appearance?: Appearance; onVisualThemeChange?: (id: string) => void }) { const [opened, setOpened] = useState(false); const root = useRef(null); const button = useRef(null); const timer = useRef | null>(null); const cancelClose = () => { if (timer.current !== null) clearTimeout(timer.current); timer.current = null; }; const close = () => { cancelClose(); setOpened(false); }; const open = () => { cancelClose(); setOpened(true); }; const enter = (event: React.PointerEvent) => { if (event.pointerType !== "touch") open(); }; const leave = (event: React.PointerEvent) => { if (event.pointerType === "touch") return; cancelClose(); timer.current = setTimeout(close, 200); }; useEffect(() => cancelClose, []); useEffect(() => { if (!opened) return; const outside = (event: PointerEvent) => { if (!root.current?.contains(event.target as Node)) close(); }; const escape = (event: KeyboardEvent) => { if (event.key === "Escape") { close(); button.current?.focus(); } }; document.addEventListener("pointerdown", outside); document.addEventListener("keydown", escape); return () => { document.removeEventListener("pointerdown", outside); document.removeEventListener("keydown", escape); }; }, [opened]); return
{opened &&

Appearance

Theme

{ if (!["ArrowUp", "ArrowDown", "Home", "End"].includes(event.key)) return; const buttons = Array.from(event.currentTarget.querySelectorAll('button[role="radio"]')); const current = buttons.indexOf(event.target as HTMLButtonElement); if (current < 0) return; event.preventDefault(); const index = event.key === "Home" ? 0 : event.key === "End" ? buttons.length - 1 : (current + (event.key === "ArrowDown" ? 1 : -1) + buttons.length) % buttons.length; buttons[index].focus(); buttons[index].click(); }}> {VISUAL_THEMES.filter(item => item.variants[appearance]).sort((a, b) => a.id === "chatgpt" ? -1 : b.id === "chatgpt" ? 1 : a.label.localeCompare(b.label)).map((item, index) => { const colors = themeVariables(item, appearance); return
{index === 1 &&
Codex themes
}
; })}
}
; }