import { Icon } from "@icones/react" import { usePreference } from "@workspace/preferences" import { Container, SiteFooter, SiteHeader, } from "../components/layout" import { LocaleFlag } from "../components/locale-flag" import { SearchTrigger } from "../components/search-trigger" import { Swatch } from "../components/swatch" import { getLocalizedHref } from "../content/registry" import type { DocLocale } from "../content/types" import { accentColors, backgroundShades, getPaletteColor, themeModes, useAppearance, type AccentColor, type BackgroundShade, type ForegroundShade, type ThemeMode, } from "../lib/appearance" import { useLocale, useTranslate, type Translation } from "../lib/locale" import { navigateToLocale } from "../lib/locale-navigation" import { detectLocale } from "../lib/preferences" import { usePathname } from "../lib/router" const foregroundOptions: ReadonlyArray<{ label: Translation value: ForegroundShade }> = [ { value: "slate", label: { zh: "冷色", us: "Cool" } }, { value: "neutral", label: { zh: "中性", us: "Neutral" } }, { value: "stone", label: { zh: "暖色", us: "Warm" } }, ] const messages = { accent: { zh: "强调色", us: "Accent color" }, appearance: { zh: "外观", us: "Appearance" }, autoDetect: { zh: "自动检测", us: "Automatic detection" }, autoDetectDescription: { zh: "跟随浏览器或操作系统语言设置。", us: "Follow the browser or operating system language.", }, background: { zh: "背景色调", us: "Background shade" }, enable: { zh: "启用", us: "Enable" }, foreground: { zh: "前景色调", us: "Foreground shade" }, language: { zh: "语言", us: "Language" }, settings: { zh: "设置", us: "Settings" }, theme: { zh: "主题", us: "Theme" }, themeModes: { light: { zh: "浅色", us: "Light" }, dark: { zh: "深色", us: "Dark" }, system: { zh: "跟随系统", us: "System" }, }, } as const export default function SettingsPage() { const locale = useLocale() const pathname = usePathname() const t = useTranslate() const appearance = useAppearance() const [localeMode, setLocaleMode] = usePreference("locale-mode") const [, setPreferredLocale] = usePreference("locale") const autoDetectLocale = localeMode === "auto" function selectLocale(nextLocale: DocLocale) { setPreferredLocale(nextLocale) setLocaleMode("manual") navigateToLocale(nextLocale) } function setAutomaticLocale(enabled: boolean) { if (enabled) { setLocaleMode("auto") navigateToLocale(detectLocale()) } else { setPreferredLocale(locale) setLocaleMode("manual") } } return ( <> {t(messages.settings)} } className="h-16 border-b bg-white/70 backdrop-blur-sm dark:border-slate-800 dark:bg-slate-950/80" actions={} />

{t(messages.appearance)}

{t(messages.theme)}
{ if ( event.key !== "ArrowDown" && event.key !== "ArrowUp" && event.key !== "ArrowRight" && event.key !== "ArrowLeft" && event.key !== "Home" && event.key !== "End" ) { return } event.preventDefault() const options = Array.from( event.currentTarget.querySelectorAll( '[role="radio"]' ) ) const currentIndex = options.indexOf( document.activeElement as HTMLButtonElement ) const moveForward = event.key === "ArrowDown" || event.key === "ArrowRight" let nextIndex = currentIndex if (event.key === "Home") nextIndex = 0 else if (event.key === "End") nextIndex = options.length - 1 else if (moveForward) { nextIndex = (currentIndex + 1) % options.length } else { nextIndex = (currentIndex - 1 + options.length) % options.length } options[nextIndex]?.focus() options[nextIndex]?.click() }} > {themeModes.map((mode) => { const selected = appearance.themeMode === mode return ( ) })}
({ light: getPaletteColor(accentColor, 600), dark: getPaletteColor(accentColor, 400), })} onChange={(accentColor) => appearance.updateAppearance({ accentColor }) } /> ({ light: getPaletteColor(backgroundShade, 50), dark: getPaletteColor(backgroundShade, 950), })} onChange={(backgroundShade) => appearance.updateAppearance({ backgroundShade }) } />
{t(messages.foreground)}
{foregroundOptions.map((option) => ( appearance.updateAppearance({ foregroundShade: option.value, }) } /> ))}

{t(messages.language)}

{t(messages.language)}
selectLocale("zh-Hans")} /> selectLocale("en-US")} />
{t(messages.autoDetect)}

{t(messages.autoDetectDescription)}

) } function ColorField({ getColors, label, onChange, options, value, }: { getColors: (value: T) => { dark: string; light: string } label: string onChange: (value: T) => void options: readonly T[] value: T }) { return (
{label}
{options.map((option) => { const colors = getColors(option) const optionLabel = capitalize(option) return ( onChange(option)} /> ) })}
) } function ThemeIcon({ mode }: { mode: ThemeMode }) { if (mode === "light") { return } if (mode === "dark") { return } return } function LocaleLink({ disabled, href, label, locale, onClick, selected, }: { disabled: boolean href: string label: string locale: DocLocale onClick: () => void selected: boolean }) { return ( { if (disabled) { event.preventDefault() return } onClick() }} > {label} {selected && } ) } function capitalize(value: string) { return value.charAt(0).toUpperCase() + value.slice(1) }