// dudu 设置窗口 — 四分组(输入 / 通用 / 账号与时长 / 帮助),规格见 doc/frontend-design.html 4.2 import { useEffect, useState } from 'react'; import { Button } from '@dudu/design/components/core/Button'; import { Badge } from '@dudu/design/components/core/Badge'; import { Card } from '@dudu/design/components/core/Card'; import { HotkeyCombo } from '@dudu/design/components/core/Kbd'; import { ProgressBar } from '@dudu/design/components/core/ProgressBar'; import { Switch } from '@dudu/design/components/forms/Switch'; import { SettingRow } from '@dudu/design/components/forms/SettingRow'; import { Select } from '@dudu/design/components/forms/Select'; import { invoke } from '../shared/tauri'; import { setThemePref } from '../shared/theme'; const THEME_OPTIONS = [['system', '跟随系统'], ['light', '浅色'], ['dark', '深色']]; // 分组卡片(对齐原型 SettingsTray 2026-07-11 改版):组标题在卡外、 // radius-lg 卡片体块、载入分组轻微上浮淡入(40ms 级差)。 const groupCss = ` @keyframes dd-set-in { from { opacity: 0; transform: translateY(4px); } } .dd-set-group { animation: dd-set-in var(--dur-base) var(--ease-out) both; } `; function Group({ title, order = 0, children }) { return (
{title}
{children}
); } export function SettingsApp() { const [settings, setSettings] = useState({ hotkey: 'CmdOrCtrl+Shift+Space', mic: '', sound: false, autostart: true, theme: 'system', }); const [mics, setMics] = useState([]); const [me, setMe] = useState(null); // null = 未登录 useEffect(() => { invoke('get_settings').then((s) => { if (s) { setSettings(s); setThemePref(s.theme); } }); invoke('list_mic_devices').then((d) => d && setMics(d)); invoke('fetch_me').then((m) => m && setMe(m)); }, []); // 仅这些字段属于「用户可改的设置」,回写时只动这几项, // 其余字段(token / device_id / onboarding_done / server_url)保持后端最新值。 const USER_FIELDS = ['hotkey', 'mic', 'sound', 'autostart', 'theme']; const update = (patch) => { const next = { ...settings, ...patch }; setSettings(next); if (patch.theme) setThemePref(patch.theme); // 保存前重新读最新 settings 再合并,避免用陈旧(可能空)token 覆盖登录流程写入的 token, // 导致先开设置窗再扫码登录、回设置页改开关时静默登出(丢失更新)。 invoke('get_settings').then((latest) => { const base = latest || settings; const merged = { ...base }; for (const k of USER_FIELDS) merged[k] = next[k]; invoke('set_settings', { settings: merged }); }); }; const minutes = me ? Math.floor(me.balance_seconds / 60) : 0; const trialUsedMin = me ? Math.floor(me.trial_used_today / 60) : 0; return ( // 透明窗口自绘 MacWindow 框(对齐原型):radius-md + border-1,占满窗口, // 阴影用 macOS 原生(贴合内容圆角,窗口即卡片实际尺寸)。 // 原生 NSWindow 底色在深色内容外露白边,故窗口 transparent、框由页面画。
{/* 自绘顶条:系统红绿灯悬浮于左上,标题绝对居中(mac 原生习惯,永不重叠);整条可拖拽 */}
dudu 设置