/** * Notes settings card — the `notes` entry inside the web settings surface * (设置 → 插件 → 插件配置). Follows the official plugin-card pattern: a * collapsible header (name + description + chevron) that expands into the * form. Every control writes straight back through the bound settings scope, * so changes apply to the floating UI live — no save/discard footer needed. * @module dsh-web-notes/client/NotesSettingsCard */ import { useSyncExternalStore, useState, type ReactElement } from 'react' import type { SettingsScope } from '@deepseek-ai/dsh-client-runtime/client' import type { NotesButtonSize, NotesDockMode } from './api.ts' import type { NotesUiSettings } from './settings.ts' import { DEFAULT_UI_SETTINGS } from './settings.ts' /** Props of the notes settings card (injected through the slot entry). */ export interface NotesSettingsCardProps { /** The bound `notes` settings scope (reads + writes). */ scope: SettingsScope /** Translation helper (notes locale dictionary). */ notesT: (key: string) => string } /** Segmented control — the official tab-style picker (no native select). */ function Segmented(props: { dataPart: string value: T options: readonly { value: T; label: string }[] onChange: (value: T) => void }): ReactElement { return (
{props.options.map((option) => ( ))}
) } /** The notes plugin card in 设置 → 插件 → 插件配置. */ export function NotesSettingsCard(props: NotesSettingsCardProps): ReactElement | null { const { scope, notesT } = props // Reactively mirror the scope: external edits (another surface, or the Host // document changing) refresh the controls in place. const snapshot = useSyncExternalStore( (listener) => scope.subscribe(listener), () => scope.getSnapshot(), ) if (snapshot.status === 'unavailable') return null const value = snapshot.value ?? DEFAULT_UI_SETTINGS // Collapsed by default, exactly like the official plugin cards. const [open, setOpen] = useState(false) const setDockMode = (next: NotesDockMode): void => { void scope.set('dockMode', next) } const setButtonSize = (next: NotesButtonSize): void => { void scope.set('buttonSize', next) } const setDefaultGlobal = (checked: boolean): void => { if (checked) void scope.set('defaultGlobal', true) else void scope.unset('defaultGlobal') } return (
  • {open ? (
    {/* 快捷入口: segmented picker (floating / fixed), no parenthetical hints */}
    {notesT('notes.settings.dockMode')}
    dataPart="notes-setting-dockMode" value={value.dockMode} onChange={(next) => { setDockMode(next) }} options={[ { value: 'floating', label: notesT('notes.settings.dockModeFloating') }, { value: 'fixed', label: notesT('notes.settings.dockModeFixed') }, ]} />
    {/* 按钮大小: three-step segmented bar */}
    {notesT('notes.settings.buttonSize')}
    dataPart="notes-setting-buttonSize" value={value.buttonSize} onChange={(next) => { setButtonSize(next) }} options={[ { value: 'small', label: notesT('notes.settings.buttonSizeSmall') }, { value: 'regular', label: notesT('notes.settings.buttonSizeRegular') }, { value: 'large', label: notesT('notes.settings.buttonSizeLarge') }, ]} />
    {/* 默认全局: checkbox on the right, aligned with official controls */}
    {notesT('notes.settings.defaultGlobal')}
    ) : null}
  • ) }