refactor(settings): migrate controls to shared primitives
This commit is contained in:
1 parent
0284603847
commit
fb7e2eb3da
2 files changed
+38
-60
No files matched your search
@@ -21,6 +21,9 @@
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import type { ReactNode } from 'react'
|
||||
import type { GeneralSettingsView } from '@shared/types'
|
||||
import { Button } from '../ui/Button'
|
||||
import { Input } from '../ui/Input'
|
||||
import { Switch } from '../ui/Switch'
|
||||
import { IconTaskOpenFolder } from './task-menu-icons'
|
||||
import { IconNotificationAuthorize, IconSelectCaret } from './general-settings-icons'
|
||||
import './source-general-settings.css'
|
||||
@@ -110,40 +113,6 @@ function Section({ title, desc, action, body, stacked, className }: SectionProps
|
||||
)
|
||||
}
|
||||
|
||||
function Switch({ checked, ariaLabel, disabled, onChange }: {
|
||||
checked: boolean
|
||||
ariaLabel: string
|
||||
disabled?: boolean
|
||||
onChange(checked: boolean): void
|
||||
}): React.JSX.Element {
|
||||
return (
|
||||
<div className="general-settings-panel__switch-wrap">
|
||||
<label className="wb-switch wb-switch--large">
|
||||
<span className="wb-switch__control">
|
||||
<input className="wb-switch__input" type="checkbox" role="switch" aria-label={ariaLabel} checked={checked}
|
||||
disabled={disabled} onChange={(event) => onChange(event.target.checked)} />
|
||||
<span className="wb-switch__track"><span className="wb-switch__thumb" /></span>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
/** 未接入的开关:保持原版开/关外观与尺寸,但不可交互。 */
|
||||
function UnavailableSwitch({ checked, ariaLabel }: { checked: boolean; ariaLabel: string }): React.JSX.Element {
|
||||
return (
|
||||
<div className="general-settings-panel__switch-wrap" data-settings-unavailable="true" aria-disabled="true" title={UNAVAILABLE_HINT}>
|
||||
<label className="wb-switch wb-switch--large" aria-disabled="true">
|
||||
<span className="wb-switch__control">
|
||||
<input className="wb-switch__input" type="checkbox" role="switch" aria-label={ariaLabel} checked={checked}
|
||||
readOnly tabIndex={-1} aria-disabled="true" />
|
||||
<span className="wb-switch__track"><span className="wb-switch__thumb" /></span>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
/** 未接入的下拉:原版 120×32 闭合态(value + caret),不可展开。 */
|
||||
function UnavailableSelect({ value, className, ariaLabel }: { value: string; className?: string; ariaLabel: string }): React.JSX.Element {
|
||||
return (
|
||||
@@ -239,21 +208,27 @@ export default function GeneralSettingsPanel(): React.JSX.Element {
|
||||
<Section title="允许锁屏运行" desc="选择锁屏后的运行方式,保障远程控制与后台 Agent 任务持续执行。"
|
||||
action={<UnavailableSelect value="关闭" ariaLabel="允许锁屏运行" className="power-save-blocker-settings__select" />} />
|
||||
<Section title="运行时防止系统休眠" desc="在 YCode 运行任务时,让电脑保持唤醒状态。"
|
||||
action={<Switch checked={view?.preventSleep ?? true} ariaLabel="运行时防止系统休眠" disabled={busy || !view}
|
||||
onChange={(checked) => update({ preventSleep: checked })} />} />
|
||||
action={<Switch checked={view?.preventSleep ?? true} aria-label="运行时防止系统休眠" disabled={busy || !view} size="lg"
|
||||
onCheckedChange={(checked) => update({ preventSleep: checked })} />} />
|
||||
<Section title="开机自动启动" desc="开启后 YCode 会在你登录电脑后自动启动。"
|
||||
action={<Switch checked={view?.autoLaunch ?? false} ariaLabel="开机自动启动" disabled={busy || !view}
|
||||
onChange={(checked) => update({ autoLaunch: checked })} />} />
|
||||
action={<Switch checked={view?.autoLaunch ?? false} aria-label="开机自动启动" disabled={busy || !view} size="lg"
|
||||
onCheckedChange={(checked) => update({ autoLaunch: checked })} />} />
|
||||
<Section title="网络代理" desc="网络异常时可切换连接方式,修改后立即生效" stacked
|
||||
className="general-settings-panel__section--proxy"
|
||||
action={<UnavailableSelect value="直接连接" ariaLabel="网络代理" />}
|
||||
body={<div className="proxy-section" />} />
|
||||
<Section title="自动安装可信技能" desc="安全检测通过后自动安装,高风险项始终要求手动确认。"
|
||||
action={<UnavailableSwitch checked={false} ariaLabel="自动安装可信技能" />} />
|
||||
action={<div className="general-settings-panel__switch-wrap" data-settings-unavailable="true" aria-disabled="true" title={UNAVAILABLE_HINT}>
|
||||
<Switch checked={false} readOnly aria-label="自动安装可信技能" size="lg" />
|
||||
</div>} />
|
||||
<Section title="技能自动更新" desc="开启后将自动更新已安装的技能为最新版本,不会更新你在 YCode 中编辑过的技能"
|
||||
action={<UnavailableSwitch checked ariaLabel="技能自动更新" />} />
|
||||
action={<div className="general-settings-panel__switch-wrap" data-settings-unavailable="true" aria-disabled="true" title={UNAVAILABLE_HINT}>
|
||||
<Switch checked readOnly aria-label="技能自动更新" size="lg" />
|
||||
</div>} />
|
||||
<Section title="套件自动更新" desc="开启后将自动更新已安装的套件为最新版本。"
|
||||
action={<UnavailableSwitch checked ariaLabel="套件自动更新" />} />
|
||||
action={<div className="general-settings-panel__switch-wrap" data-settings-unavailable="true" aria-disabled="true" title={UNAVAILABLE_HINT}>
|
||||
<Switch checked readOnly aria-label="套件自动更新" size="lg" />
|
||||
</div>} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -263,11 +238,10 @@ export default function GeneralSettingsPanel(): React.JSX.Element {
|
||||
<div className="wb-storage__cache">
|
||||
<div className="wb-storage__card-title-row">
|
||||
<div className="wb-storage__card-title">系统缓存目录</div>
|
||||
<button type="button" className="wb-button wb-button--ghost wb-button--medium wb-storage__open-folder"
|
||||
onClick={() => openFolder('data')}>
|
||||
<span className="wb-button__icon wb-button__icon--left"><IconTaskOpenFolder className="wb-icon" /></span>
|
||||
<span className="wb-button__content">打开目录</span>
|
||||
</button>
|
||||
<Button variant="ghost" size="md" iconLeft={<IconTaskOpenFolder />}
|
||||
className="wb-storage__open-folder" onClick={() => openFolder('data')}>
|
||||
打开目录
|
||||
</Button>
|
||||
</div>
|
||||
<div className="wb-storage__path-row">
|
||||
<span className="wb-storage__path-text">{view?.dataDir ?? '—'} · 存放对话记录、运行缓存与临时文件。</span>
|
||||
@@ -310,12 +284,9 @@ export default function GeneralSettingsPanel(): React.JSX.Element {
|
||||
</div>
|
||||
<p className="general-settings-panel__workspace-path-desc">新建任务、工作空间时将自动存放在该路径下;修改后不影响已有数据。</p>
|
||||
<div className="general-settings-panel__path-row">
|
||||
<input className="wb-input wb-input--medium wb-input--filled general-settings-panel__path-input"
|
||||
aria-label="默认工作空间存储路径" value={view?.workspaceRoot ?? ''} readOnly />
|
||||
<button type="button" className="wb-button wb-button--secondary wb-button--medium" disabled={busy || !view}
|
||||
onClick={changeWorkspaceRoot}>
|
||||
<span className="wb-button__content">更改</span>
|
||||
</button>
|
||||
<Input size="md" filled readOnly wrapperClassName="general-settings-panel__path-input"
|
||||
aria-label="默认工作空间存储路径" value={view?.workspaceRoot ?? ''} />
|
||||
<Button variant="secondary" size="md" disabled={busy || !view} onClick={changeWorkspaceRoot}>更改</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -325,15 +296,14 @@ export default function GeneralSettingsPanel(): React.JSX.Element {
|
||||
<p className="general-settings-panel__group-label">通知</p>
|
||||
<div className="general-settings-panel__group-card">
|
||||
<Section title="桌面通知" desc="任务完成或有新消息时,将会通过系统弹窗提醒你。"
|
||||
action={<button type="button" className="wb-button wb-button--secondary wb-button--medium"
|
||||
onClick={openNotificationSettings}>
|
||||
<span className="wb-button__content">前往系统设置</span>
|
||||
<span className="wb-button__icon wb-button__icon--right"><IconNotificationAuthorize /></span>
|
||||
</button>} />
|
||||
action={<Button variant="secondary" size="md" iconRight={<IconNotificationAuthorize />}
|
||||
onClick={openNotificationSettings}>前往系统设置</Button>} />
|
||||
<Section title="通知提示音" desc="收到桌面或应用内通知时播放的提示音。"
|
||||
action={<UnavailableSelect value="无音效" ariaLabel="通知提示音" />} />
|
||||
<Section title="定时任务产物通知" desc="定时任务的产物生成后,将会通过系统弹窗提醒你。"
|
||||
action={<UnavailableSwitch checked ariaLabel="定时任务产物通知" />} />
|
||||
action={<div className="general-settings-panel__switch-wrap" data-settings-unavailable="true" aria-disabled="true" title={UNAVAILABLE_HINT}>
|
||||
<Switch checked readOnly aria-label="定时任务产物通知" size="lg" />
|
||||
</div>} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -346,7 +316,9 @@ export default function GeneralSettingsPanel(): React.JSX.Element {
|
||||
{' '}<a className="general-settings-panel__inline-link" href="#none" aria-disabled="true" title={UNAVAILABLE_HINT}
|
||||
onClick={(event) => event.preventDefault()}>了解更多</a>
|
||||
</>}
|
||||
action={<UnavailableSwitch checked ariaLabel="体验优化计划" />} />
|
||||
action={<div className="general-settings-panel__switch-wrap" data-settings-unavailable="true" aria-disabled="true" title={UNAVAILABLE_HINT}>
|
||||
<Switch checked readOnly aria-label="体验优化计划" size="lg" />
|
||||
</div>} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -3,6 +3,12 @@ import { useSettingsStore } from '../stores/settings'
|
||||
import SourceSettingsNavigation from './SourceSettingsNavigation'
|
||||
import { useSharedTheme } from '../useSharedTheme'
|
||||
import { SOURCE_WINDOWS_SETTINGS_TABS } from './SourceSettingsNavigation'
|
||||
import { Button } from '../ui/Button'
|
||||
// 统一组件样式必须在恢复样式之前加载:恢复样式是页面作用域的覆盖层,
|
||||
// 同权重时后写者胜,顺序反转会让 `.yc-*` 默认值盖掉 `.general-settings-panel__*` 的页面覆盖。
|
||||
// `../ui/Button` 已把 button.css 提前带入,这里补上其余两张组件表。
|
||||
import '../ui/input.css'
|
||||
import '../ui/switch.css'
|
||||
// Shared UI-kit controls are also used by direct links to Memory/Agents/About.
|
||||
import './source-select/source-select-recovered.css'
|
||||
import './source-select/source-select.css'
|
||||
@@ -77,7 +83,7 @@ export default function SettingsDialog({ theme, onThemeChange, onClose, nativeWi
|
||||
{error && <p role="alert">{error}</p>}
|
||||
{section === 'settings' && <GeneralSettingsPanel />}
|
||||
{section === 'model' && <ModelSettingsPanel models={settings.models} createRequest={createRequest} onCreateHandled={() => setCreateRequest(0)} onSave={settings.saveModel} onDelete={settings.deleteModel} onTest={(input) => window.ycode.models.test(input)} />}
|
||||
{section === 'workspace' && <div className="settings-window-panel"><h2>工作空间</h2>{settings.workspaces.map((space) => <button className="btn" key={space.id} title={space.root} onClick={() => void window.ycode.workspace.open(space.id).catch((error) => setError(String(error.message ?? error)))}>{space.name}</button>)}<button className="btn" onClick={() => void settings.attachWorkspace().catch((error) => setError(String(error.message ?? error)))}>打开已有文件夹</button></div>}
|
||||
{section === 'workspace' && <div className="settings-window-panel"><h2>工作空间</h2>{settings.workspaces.map((space) => <Button variant="secondary" size="md" key={space.id} title={space.root} onClick={() => void window.ycode.workspace.open(space.id).catch((error) => setError(String(error.message ?? error)))}>{space.name}</Button>)}<Button variant="secondary" size="md" onClick={() => void settings.attachWorkspace().catch((error) => setError(String(error.message ?? error)))}>打开已有文件夹</Button></div>}
|
||||
{section === 'appearance' && <AppearanceSettingsPanel theme={theme} onThemeChange={onThemeChange} />}
|
||||
{section === 'keyboardShortcuts' && <ShortcutsSettingsPanel />}
|
||||
{section === 'personalization' && <PersonalizationSettingsPanel />}
|
||||
|
||||
Reference in new issue
Block a user