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.
This commit is contained in:
@@ -24,7 +24,7 @@
|
||||
},
|
||||
"dshClient": {
|
||||
"inject": [
|
||||
"@deepseek-ai/dsh-client-i18n",
|
||||
"@deepseek-ai/dsh-client-locale",
|
||||
"@deepseek-ai/dsh-client-runtime",
|
||||
"@deepseek-ai/dsh-client-ui-layout"
|
||||
],
|
||||
|
||||
@@ -99,7 +99,7 @@ async function bench() {
|
||||
ctx.provide('workspaces', workspacesFake)
|
||||
const layoutFake = { openDetails: vi.fn(), closeDetails: vi.fn() }
|
||||
ctx.provide('layout', layoutFake)
|
||||
ctx.provide('i18n', { bind: () => (key: string) => key })
|
||||
ctx.provide('locale', { bind: () => (key: string) => key })
|
||||
|
||||
// The AppFrame role: the three conversation-package slots must be declared
|
||||
// by a live entry before apply can contribute into them (the stand-in
|
||||
|
||||
@@ -48,7 +48,7 @@ async function bench() {
|
||||
sendSession: vi.fn(),
|
||||
})
|
||||
ctx.provide('layout', { openDetails: vi.fn(), closeDetails: vi.fn() })
|
||||
ctx.provide('i18n', { bind: () => (key: string) => key })
|
||||
ctx.provide('locale', { bind: () => (key: string) => key })
|
||||
|
||||
// Declared by ui-layout's root entry in production; a stand-in root
|
||||
// occupant declares them here so the contributions land (it consumes
|
||||
|
||||
@@ -93,7 +93,7 @@ async function bench(nodes: ToolResultNode[]) {
|
||||
sendSession: vi.fn(),
|
||||
})
|
||||
ctx.provide('layout', layout)
|
||||
ctx.provide('i18n', { bind: () => (key: string) => key })
|
||||
ctx.provide('locale', { bind: () => (key: string) => key })
|
||||
|
||||
slots.install(createSlotRenderer())
|
||||
slots.register({
|
||||
@@ -212,7 +212,7 @@ describe('registrant load-order seam', () => {
|
||||
sendSession: vi.fn(),
|
||||
})
|
||||
ctx.provide('layout', { openDetails: vi.fn(), closeDetails: vi.fn() })
|
||||
ctx.provide('i18n', { bind: () => (key: string) => key })
|
||||
ctx.provide('locale', { bind: () => (key: string) => key })
|
||||
slots.register({
|
||||
name: 'root',
|
||||
children: {
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
"path": "../ui-layout"
|
||||
},
|
||||
{
|
||||
"path": "../i18n"
|
||||
"path": "../locale"
|
||||
},
|
||||
{
|
||||
"path": "../../support/invariants"
|
||||
|
||||
Reference in New Issue
Block a user