Files
dudu/design/ui_kits/desktop/SettingsTray.jsx
T
wangjia 911ceca079
ci / server (push) Failing after 11s
ci / design-system (push) Failing after 12s
feat(design+desktop): Select 真相源组件 + 设置窗口无边框(12A 实测反馈)
Select 组件(用户拍板:下拉框全端共用一个真相源):
- design/components/forms/Select.jsx:自绘下拉——触发器同 Input
  (control-sm/radius-sm/border-1 + chevronDown),弹层同菜单
  (radius-md/shadow-menu、选中项前置对勾、hover surface-2),
  外点/Esc 收起;登记簿加卡;icons.js 新增 chevronDown 并登记 icon-map
- 原型 SettingsTray 与桌面 SettingsApp 同步换用(替换原生 select——
  其系统弹层样式/位置不可控,即 12A 截图问题)

原型组件运行时可再生(修复单源缺口):
- design-pipeline/bundle-components.mjs:用 desktop 现成 esbuild 重建
  _ds_bundle.js(此前为 Claude 导出产物、仓库内无法再生,组件一改即
  过期);import 'react' 经全局 shim,命名空间不变;已实测原型页正常
- _ds_bundle.js 重建(15 个组件,含 Select/Icon)

设置窗口无边框:
- tauri.conf settings 窗口 titleBarStyle=Overlay + hiddenTitle,
  系统红绿灯悬浮左上;SettingsApp 自绘顶条(surface-2 + 下边框 +
  data-tauri-drag-region 可拖拽),对齐原型 MacWindow 规格

合并 feat/design-truth-source(真相源与闸体系)进本分支

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 01:00:29 +08:00

134 lines
6.3 KiB
React

// dudu 桌面端 · 系统托盘菜单 + 设置窗口
function TrayMenu() {
const { Badge } = window.DuduDesignSystem_2e0172;
const Item = ({ children, right, accent, onClick }) => {
const [hover, setHover] = React.useState(false);
return (
<div
onMouseEnter={() => setHover(true)} onMouseLeave={() => setHover(false)} onClick={onClick}
style={{
display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: 18,
padding: '7px 14px', cursor: 'default', fontSize: 'var(--text-sm)',
background: hover ? 'var(--accent)' : 'transparent',
color: hover ? 'var(--text-on-accent)' : (accent ? 'var(--text-1)' : 'var(--text-1)'),
transition: 'background var(--dur-fast) var(--ease-out)',
}}>
<span style={{ display: 'inline-flex', alignItems: 'center', gap: 8, whiteSpace: 'nowrap' }}>{children}</span>
{right && <span style={{ fontSize: 'var(--text-xs)', color: hover ? 'var(--text-on-accent)' : 'var(--text-3)', opacity: hover ? 0.8 : 1, whiteSpace: 'nowrap' }}>{right}</span>}
</div>
);
};
const Sep = () => <div style={{ height: 1, background: 'var(--border-1)', margin: '4px 0' }}></div>;
return (
<div style={{
width: 248, padding: '5px 0', fontFamily: 'var(--font-sans)',
background: 'color-mix(in srgb, var(--surface-card) 96%, transparent)',
backdropFilter: 'blur(20px)', WebkitBackdropFilter: 'blur(20px)',
border: '1px solid var(--border-1)', borderRadius: 'var(--radius-md)', boxShadow: 'var(--shadow-menu)',
}}>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '8px 14px 6px' }}>
<span style={{ display: 'inline-flex', alignItems: 'center', gap: 8, fontSize: 'var(--text-sm)', fontWeight: 'var(--weight-medium)', color: 'var(--text-1)', whiteSpace: 'nowrap' }}>
<DuduLogo size={17} /> dudu 语音输入
</span>
<Badge tone="green">已就绪</Badge>
</div>
<Sep />
<Item right={<DuIcons.Check size={13} />}>启用快捷键</Item>
<Item right="⌘⇧Space">按住说话</Item>
<Item>设置</Item>
<Item right="472 分钟">时长余额</Item>
<Sep />
<Item right="已登录">账号 wang***</Item>
<Item>检查更新</Item>
<Sep />
<Item>退出 dudu</Item>
</div>
);
}
function SettingsWindow({ theme, onThemeChange }) {
const { Switch, SettingRow, HotkeyCombo, Button, Badge, ProgressBar, Select } = window.DuduDesignSystem_2e0172;
const [autostart, setAutostart] = React.useState(true);
const [sound, setSound] = React.useState(false);
const [mic, setMic] = React.useState('MacBook Pro 麦克风');
const Section = ({ title, children }) => (
<div style={{ padding: '4px 18px 8px' }}>
<div style={{ fontSize: 'var(--text-xs)', color: 'var(--text-3)', fontWeight: 'var(--weight-medium)', margin: '10px 0 2px', letterSpacing: 'var(--tracking-wide)' }}>{title}</div>
{children}
</div>
);
const themeOptions = [['system', '跟随系统'], ['light', '浅色'], ['dark', '深色']];
return (
<MacWindow title="dudu 设置" width={440}>
<Section title="输入">
<SettingRow label="说话快捷键" description="按住说话,松开上屏">
<span style={{ display: 'inline-flex', alignItems: 'center', gap: 10 }}>
<HotkeyCombo keys={['⌘', '⇧', 'Space']} />
<Button variant="ghost" size="sm">修改</Button>
</span>
</SettingRow>
<SettingRow label="麦克风">
<Select
value={mic}
onChange={setMic}
options={['MacBook Pro 麦克风', 'AirPods Pro', '外接 USB 麦克风']}
/>
</SettingRow>
<SettingRow label="提示音" description="开始 / 完成时播放轻提示音">
<Switch checked={sound} onChange={setSound} />
</SettingRow>
</Section>
<div style={{ height: 1, background: 'var(--border-1)' }}></div>
<Section title="通用">
<SettingRow label="开机自启">
<Switch checked={autostart} onChange={setAutostart} />
</SettingRow>
<SettingRow label="外观">
<span style={{ display: 'inline-flex', background: 'var(--surface-2)', borderRadius: 'var(--radius-sm)', padding: 2, gap: 2 }}>
{themeOptions.map(([val, lab]) => (
<button key={val} type="button" onClick={() => onThemeChange(val)} style={{
height: 24, padding: '0 10px', borderRadius: 4, border: 'none', cursor: 'pointer',
fontSize: 'var(--text-xs)', fontFamily: 'var(--font-sans)',
background: theme === val ? 'var(--surface-card)' : 'transparent',
color: theme === val ? 'var(--text-1)' : 'var(--text-2)',
boxShadow: theme === val ? 'var(--shadow-card)' : 'none',
transition: 'background var(--dur-fast) var(--ease-out)',
}}>{lab}</button>
))}
</span>
</SettingRow>
</Section>
<div style={{ height: 1, background: 'var(--border-1)' }}></div>
<Section title="账号与时长">
<SettingRow label="登录状态" description="微信已登录">
<span style={{ display: 'inline-flex', alignItems: 'center', gap: 8, fontSize: 'var(--text-sm)', color: 'var(--text-1)' }}>
wang*** <Badge tone="green">余额充足</Badge>
</span>
</SettingRow>
<SettingRow label="时长余额" description="时长不过期 · 每天另有 3 分钟免费试用">
<span style={{ display: 'inline-flex', alignItems: 'center', gap: 10 }}>
<span style={{ fontSize: 'var(--text-base)', fontWeight: 'var(--weight-semibold)', color: 'var(--text-1)', fontFamily: 'var(--font-sans)' }}>472 分钟</span>
<Button variant="secondary" size="sm">购买时长</Button>
</span>
</SettingRow>
<div style={{ padding: '10px 0 12px' }}>
<div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 'var(--text-xs)', color: 'var(--text-2)', marginBottom: 7 }}>
<span>今日免费试用</span><span style={{ fontFamily: 'var(--font-mono)' }}>1 / 3 分钟</span>
</div>
<ProgressBar value={1} max={3} />
</div>
</Section>
</MacWindow>
);
}
Object.assign(window, { TrayMenu, SettingsWindow });