Files
deepseek-harness/packages/client/ui-settings-general/src/client/contract.ts
imccyu 6e721b9fdd feat(gui): settings panel with locale and theme preferences
Add the browser Settings surface as slot-composed plugins over new
preference services:

- Rename dsh-client-i18n to dsh-client-locale (locale is the domain
  name); LocaleService adds getLocale()/setLocale(id), immutable
  snapshots, a locale/change event, and dsh.locale persistence.
- ThemeService owns the light/dark/system preference (default system),
  resolves system via prefers-color-scheme, publishes theme/change
  snapshots, persists dsh.theme, and no longer touches the DOM;
  ui-layout's ThemePresenter applies resolved snapshots
  (body[data-ds-dark-theme] + alias tokens) and cleans up on dispose.
- ui-sidebar drops the phase-1 settings dropdown/modal; the foot renders
  the new sidebar.settings slot with the column state.
- New ui-settings shell occupies sidebar.settings: foot trigger row and
  the centered 1080x700 panel (figma 501:29947) with 24% mask, close
  button / mask click / Escape all closing, and a 188px nav projected
  from the settings.section list slot it declares. Nav labels are
  registrant-localized; sections re-register on locale change, so the
  ledger version is the shell's only subscription.
- ui-settings-general registers the General section: Permission and
  Tool Call skeletons, live Language (locale menu) and Appearance
  (Light/Dark/System cubes following the persisted preference); its
  slot store mirrors both service snapshots via apply-side listeners.
- ui-settings-models registers the Models nav entry with an empty
  content column.
- Portaled menus pin z-index above modal overlays (a menu anchored
  inside the settings dialog rendered underneath it and was
  unclickable).
- theme/data/list-pen icons in ui-primitives; settings copy ships as
  zh/en dictionaries; fixture manifests gain the settings rows.
2026-07-26 00:28:43 +08:00

67 lines
2.9 KiB
TypeScript

/**
* General section component contract: the slot-store state shape, the
* injected business face, and the composed props type. The component imports
* only from here; service snapshot shapes are mirrored as plain rows so the
* presentation layer stays decoupled from the locale/theme packages.
*/
import type { PropsRuntime, PropsStore } from '@deepseek-ai/dsh-client-ui-slots'
// Type-only: pulls the shell's SlotMap merge (the 'settings.section' entry).
import type {} from '@deepseek-ai/dsh-client-ui-settings/client'
import type { createGeneralSettingsStore } from './store.ts'
/** One selectable locale row projected into the store (id + self-described label). */
export interface LocaleOptionRow {
/** Locale id (the setLocale argument). */
id: string
/** Display name in its own language (中文 / English). */
label: string
}
/** Theme preference union mirrored from the theme service snapshot. */
export type ThemePreferenceId = 'light' | 'dark' | 'system'
/**
* Store state: mirrors of the locale/theme service snapshots, written only by
* the plugin's apply-world change listeners (components have no write path —
* preference writes go through the injected callbacks to the services, and
* the resulting change events flow back into this mirror).
*/
export interface GeneralSettingsState {
/** Active locale id. */
localeActive: string
/** Selectable locales in display order. */
localeOptions: LocaleOptionRow[]
/** Locale service revision (re-renders translated copy on dictionary/locale changes); -1 until first sync. */
localeRevision: number
/** Persisted theme preference (selection state reads this, never the resolved active theme). */
themePreference: ThemePreferenceId
/** Theme service revision; -1 until first sync. */
themeRevision: number
}
/**
* Registrant-private injected share of the General section (assembled in
* apply): the namespace-bound translate function (stable identity — re-render
* on locale change comes from the store revision, not from `t`) and the two
* preference write callbacks.
*/
export interface GeneralSectionInjected {
/** Translate a `settings.general` dictionary key to the active-locale text. */
t: (key: string) => string
/** Switch the active locale (a registered locale id). */
setLocale: (id: string) => void
/** Switch the theme preference. */
setTheme: (id: ThemePreferenceId) => void
}
/** Store handle type for the props share (type-only; the factory stays internal to apply and tests). */
export type GeneralSettingsStoreHandle = ReturnType<typeof createGeneralSettingsStore>
/**
* Full component props of the General section: the section owner share
* (empty marker) plus the store share and the injected face. No child slots
* are declared; menu open state is component-local viewing state.
*/
export type GeneralSectionComponentProps =
PropsRuntime<'settings.section'> & PropsStore<GeneralSettingsStoreHandle> & GeneralSectionInjected