/** * AppearanceSettings — 外观设置 Tab * * 从 SettingsModal.tsx 提取(v0.4.1 拆分)。 * 功能:主题切换(深色/浅色/跟随系统)+ 界面语言切换(v0.7.2 P4-15)。 * * 语言切换契约: * - 偏好持久化在 ui.locale(全局配置层,跨工作空间共享) * - 切换即时生效(setLocale 触发 i18next languageChanged) * - 启动时由 App.tsx 读取 ui.locale 并应用(默认 zh-CN) */ import { Button, Stack, Typography, Box, Switch, FormControlLabel } from '@mui/material'; import { useEffect, useState } from 'react'; import type { ThemeMode } from '@renderer/stores/ui-store'; import { useConfig } from './useConfig'; import { setLocale, getLocale, type Locale } from '@renderer/lib/i18n'; // v0.7.4 P3-1: 文案出层(字典含注册副作用,须在 t() 使用前 import) import { t } from '@renderer/lib/i18n'; import '@renderer/lib/i18n-strings'; // v0.7.4 P3-1: 语言标签渲染时求值(t() 不能在模块顶层固化) const LOCALE_VALUES: Locale[] = ['zh-CN', 'en-US']; function localeLabel(value: Locale): string { return value === 'zh-CN' ? t('settings.appearance.localeZhCN') : t('settings.appearance.localeEnUS'); } export function AppearanceSettings({ theme, setTheme, }: { theme: ThemeMode; setTheme: (t: ThemeMode) => void; }) { // v0.7.2 P4-15: 界面语言(ui.locale 全局配置;useConfig 已含失败回滚与竞态保护) const [locale, setLocaleConfig] = useConfig('ui.locale', 'zh-CN'); // ===== v0.8.1 P2-4: 开机自启(app.setLoginItemSettings 封装;读回真实生效状态) ===== const [openAtLogin, setOpenAtLogin] = useState(false); const handleLocaleChange = (next: Locale): void => { if (next === getLocale()) return; setLocaleConfig(next); void setLocale(next); }; useEffect(() => { window.metona?.app ?.getLoginItem?.() .then((r) => { if (r.success && r.data) setOpenAtLogin(r.data.openAtLogin); }) .catch(() => {}); }, []); const handleLoginItem = (enabled: boolean): void => { setOpenAtLogin(enabled); // 乐观更新 window.metona?.app ?.setLoginItem?.(enabled) .then((r) => { if (r.success && r.data) { // 以平台真实生效状态为准(Linux 不可用时保持 false) setOpenAtLogin(r.data.openAtLogin); if (enabled && !r.data.openAtLogin) { import('@metona-team/metona-toast') .then((mod) => mod.default.warning(t('settings.appearance.autoStart.unsupported'))) .catch(() => {}); } } else { setOpenAtLogin(!enabled); // 回滚 } }) .catch(() => setOpenAtLogin(!enabled)); }; return ( {t('settings.appearance.title')} {t('settings.appearance.theme')} {(['dark', 'light', 'auto'] as ThemeMode[]).map((t_) => ( ))} {t('settings.appearance.locale')} {LOCALE_VALUES.map((v) => ( ))} {t('settings.appearance.localeHelper')} {/* ===== v0.8.1 P2-4: 开机自启 ===== */} handleLoginItem(e.target.checked)} /> } label={ {t('settings.appearance.autoStart')} {t('settings.appearance.autoStartHelper')} } sx={{ alignItems: 'flex-start', m: 0 }} /> ); }