Merge remote-tracking branch 'origin/master' into feature/gui-first-run-welcome
# Conflicts: # packages/client/ui-settings-general/src/client/index.ts # packages/client/ui-settings-general/src/client/locales.ts # packages/client/ui-settings-general/tests/apply.spec.ts # packages/client/ui-settings/README.i18n.yaml # packages/client/ui-settings/README.md # packages/client/ui-settings/README.zh.md # packages/client/ui-settings/src/client/index.ts # packages/host/apiproxy/README.i18n.yaml # packages/host/apiproxy/src/api-proxy.ts
This commit is contained in:
@@ -6,18 +6,12 @@
|
||||
* separator.
|
||||
*/
|
||||
import { IconChevronDownOutline14 } from '@deepseek-ai/dsh-client-ui-primitives'
|
||||
import type { PropsRenderSlots, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots'
|
||||
import type { PropsLocale, PropsRenderSlots, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots'
|
||||
import css from './GeneralSection.module.css'
|
||||
|
||||
/** Injected face of the General section: the settings-namespace translate. */
|
||||
export interface GeneralSectionInjected {
|
||||
/** Translate a `settings` dictionary key to the active-locale text. */
|
||||
t: (key: string) => string
|
||||
}
|
||||
|
||||
/** Full component props: section owner share + item render share + inject face. */
|
||||
/** Full component props: section owner share + item render share + the standard locale seat. */
|
||||
export type GeneralSectionComponentProps =
|
||||
PropsRuntime<'settings.section'> & PropsRenderSlots<'settings.general.item'> & GeneralSectionInjected
|
||||
PropsRuntime<'settings.section'> & PropsRenderSlots<'settings.general.item'> & PropsLocale<'settings'>
|
||||
|
||||
/**
|
||||
* Render the General section content column.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import { useCallback, useEffect, useRef } from 'react'
|
||||
import type { ReactNode } from 'react'
|
||||
import type { PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots'
|
||||
import type { PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots'
|
||||
import { BrandWordmark, Button } from '@deepseek-ai/dsh-client-ui-primitives'
|
||||
import type { SnapshotSelectorHook } from '@deepseek-ai/dsh-client-web-react'
|
||||
import type { WelcomeNoticeState, WelcomeNoticeStore } from './welcome-store.ts'
|
||||
@@ -25,11 +25,11 @@ function emphasizedFeedback(paragraph: string, emphasis: string): ReactNode {
|
||||
export interface WelcomeNoticeInjected {
|
||||
controller: WelcomeNoticeStore
|
||||
useSnapshot: SnapshotSelectorHook<WelcomeNoticeState>
|
||||
t: (key: string) => string
|
||||
}
|
||||
|
||||
/** Coordinator owner props plus the welcome step's injected face. */
|
||||
export type WelcomeNoticeProps = PropsRuntime<'settings.onboarding'> & WelcomeNoticeInjected
|
||||
export type WelcomeNoticeProps =
|
||||
PropsRuntime<'settings.onboarding'> & PropsLocale<'settings'> & WelcomeNoticeInjected
|
||||
|
||||
/** Render the mandatory notice until its current version commits durably. */
|
||||
export function WelcomeNotice(props: WelcomeNoticeProps): ReactNode {
|
||||
|
||||
@@ -5,20 +5,14 @@
|
||||
* reads each entry's `label` option for aria text.
|
||||
*/
|
||||
import { IconSettingsOutline14 } from '@deepseek-ai/dsh-client-ui-primitives'
|
||||
import type { PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots'
|
||||
import type { PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots'
|
||||
import css from './chrome.module.css'
|
||||
|
||||
/** Injected face of both chrome seats: the settings-namespace translate. */
|
||||
export interface ChromeInjected {
|
||||
/** Translate a `settings` dictionary key to the active-locale text. */
|
||||
t: (key: string) => string
|
||||
}
|
||||
/** Trigger content props: the sidebar column state + the standard locale seat. */
|
||||
export type TriggerContentProps = PropsRuntime<'settings.trigger'> & PropsLocale<'settings'>
|
||||
|
||||
/** Trigger content props: the sidebar column state + translate. */
|
||||
export type TriggerContentProps = PropsRuntime<'settings.trigger'> & ChromeInjected
|
||||
|
||||
/** Header content props: translate only. */
|
||||
export type HeaderContentProps = PropsRuntime<'settings.header'> & ChromeInjected
|
||||
/** Header content props: the standard locale seat only. */
|
||||
export type HeaderContentProps = PropsRuntime<'settings.header'> & PropsLocale<'settings'>
|
||||
|
||||
/**
|
||||
* Render the trigger row content (icon; label only in the wide column).
|
||||
@@ -43,8 +37,8 @@ export function HeaderContent({ t }: HeaderContentProps) {
|
||||
return <>{t('title')}</>
|
||||
}
|
||||
|
||||
/** Close-button label text props: translate only. */
|
||||
export type CloseLabelProps = PropsRuntime<'settings.close'> & ChromeInjected
|
||||
/** Close-button label text props: the standard locale seat only. */
|
||||
export type CloseLabelProps = PropsRuntime<'settings.close'> & PropsLocale<'settings'>
|
||||
|
||||
/**
|
||||
* Render the close button's visually-hidden label text.
|
||||
|
||||
@@ -12,24 +12,30 @@ import type { ConnectionHandle } from '@deepseek-ai/dsh-client-connection/client
|
||||
import { bindSnapshotSelector } from '@deepseek-ai/dsh-client-web-react'
|
||||
// Type-only: pulls the shell's SlotMap merges (trigger/header/section/item).
|
||||
import type {} from '@deepseek-ai/dsh-client-ui-settings/client'
|
||||
import type { ChromeInjected } from './chrome.tsx'
|
||||
// Type-only: pulls ctx.locale and the 'settings.general.item' SlotMap merge.
|
||||
import type {} from '@deepseek-ai/dsh-client-locale/client'
|
||||
import { CloseLabel, HeaderContent, TriggerContent } from './chrome.tsx'
|
||||
import type { GeneralSectionInjected } from './GeneralSection.tsx'
|
||||
import { GeneralSection } from './GeneralSection.tsx'
|
||||
import type { WelcomeNoticeInjected } from './WelcomeNotice.tsx'
|
||||
import { WelcomeNotice } from './WelcomeNotice.tsx'
|
||||
import { refreshWelcomeIfLoaded, WelcomeNoticeStore } from './welcome-store.ts'
|
||||
import { en, zh } from './locales.ts'
|
||||
import { WELCOME_NOTICE_SETTINGS_NAMESPACE } from '../onboarding-copy.ts'
|
||||
import { en, zh, type SettingsKey } from './locales.ts'
|
||||
|
||||
export type {
|
||||
ChromeInjected, CloseLabelProps, HeaderContentProps, TriggerContentProps,
|
||||
CloseLabelProps, HeaderContentProps, TriggerContentProps,
|
||||
} from './chrome.tsx'
|
||||
export type {
|
||||
GeneralSectionComponentProps, GeneralSectionInjected,
|
||||
} from './GeneralSection.tsx'
|
||||
export type { GeneralSectionComponentProps } from './GeneralSection.tsx'
|
||||
export type { WelcomeNoticeInjected, WelcomeNoticeProps } from './WelcomeNotice.tsx'
|
||||
export type { WelcomeNoticeState } from './welcome-store.ts'
|
||||
export type { SettingsKey } from './locales.ts'
|
||||
|
||||
declare module '@deepseek-ai/dsh-client-ui-slots' {
|
||||
interface LocaleNamespaceMap {
|
||||
/** Shell chrome + shell-owned General section copy. */
|
||||
settings: SettingsKey
|
||||
}
|
||||
}
|
||||
|
||||
/** Dictionary namespace owned by this plugin (shell chrome + General copy). */
|
||||
const NS = 'settings'
|
||||
@@ -47,24 +53,18 @@ export const inject = ['slots', 'locale', 'connection']
|
||||
* @param ctx - client root context.
|
||||
*/
|
||||
export function apply(ctx: ClientContext): void {
|
||||
ctx.effect(() => {
|
||||
const disposers = [
|
||||
ctx.locale.register(NS, 'zh', zh),
|
||||
ctx.locale.register(NS, 'en', en),
|
||||
]
|
||||
return () => { for (const dispose of disposers) dispose() }
|
||||
}, 'ui-settings-general: dictionaries')
|
||||
ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'ui-settings-general: dictionaries')
|
||||
|
||||
// Copy freshness is framework-owned: components read the standard `t`
|
||||
// seat, and the nav label is a thunk the owner resolves per render — no
|
||||
// locale/change re-registration wiring.
|
||||
const t = ctx.locale.bind(NS)
|
||||
const connection = ctx.get('connection') as ConnectionHandle
|
||||
const welcomeController = new WelcomeNoticeStore(connection.api)
|
||||
const useWelcomeSnapshot = bindSnapshotSelector(welcomeController.store)
|
||||
const chromeInjected = (): ChromeInjected => ({ t })
|
||||
const generalInjected = (): GeneralSectionInjected => ({ t })
|
||||
const welcomeInjected = (): WelcomeNoticeInjected => ({
|
||||
controller: welcomeController,
|
||||
useSnapshot: useWelcomeSnapshot,
|
||||
t,
|
||||
})
|
||||
|
||||
ctx.effect(() => {
|
||||
@@ -78,42 +78,31 @@ export function apply(ctx: ClientContext): void {
|
||||
]
|
||||
return () => { for (const dispose of disposers) dispose() }
|
||||
}, 'ui-settings-general: welcome invalidations')
|
||||
|
||||
// All four seats refresh on locale change: re-registration bumps each
|
||||
// slot's ledger version, which re-renders the outlets through their own
|
||||
// subscriptions (outlet memoization would swallow a parent-only render).
|
||||
ctx.effect(() => {
|
||||
const trigger = deferRegistration(ctx.slots, 'settings.trigger', TriggerContent, () =>
|
||||
ctx.slots.register({ name: 'settings.trigger', inject: chromeInjected }, TriggerContent))
|
||||
ctx.slots.register({ name: 'settings.trigger', locale: NS }, TriggerContent))
|
||||
const header = deferRegistration(ctx.slots, 'settings.header', HeaderContent, () =>
|
||||
ctx.slots.register({ name: 'settings.header', inject: chromeInjected }, HeaderContent))
|
||||
ctx.slots.register({ name: 'settings.header', locale: NS }, HeaderContent))
|
||||
const close = deferRegistration(ctx.slots, 'settings.close', CloseLabel, () =>
|
||||
ctx.slots.register({ name: 'settings.close', inject: chromeInjected }, CloseLabel))
|
||||
ctx.slots.register({ name: 'settings.close', locale: NS }, CloseLabel))
|
||||
const general = deferRegistration(ctx.slots, 'settings.section', GeneralSection, () =>
|
||||
ctx.slots.register({
|
||||
name: 'settings.section',
|
||||
id: 'general',
|
||||
order: 0,
|
||||
label: t('general.nav'),
|
||||
label: () => t('general.nav'),
|
||||
locale: NS,
|
||||
children: { 'settings.general.item': { kind: 'list', scope: 'root' } },
|
||||
inject: generalInjected,
|
||||
}, GeneralSection))
|
||||
const welcome = deferRegistration(ctx.slots, 'settings.onboarding', WelcomeNotice, () =>
|
||||
ctx.slots.register({
|
||||
name: 'settings.onboarding',
|
||||
id: 'welcome-notice',
|
||||
order: -100,
|
||||
locale: NS,
|
||||
inject: welcomeInjected,
|
||||
}, WelcomeNotice))
|
||||
const offLocale = ctx.on('locale/change', () => {
|
||||
trigger.refresh()
|
||||
header.refresh()
|
||||
close.refresh()
|
||||
general.refresh()
|
||||
welcome.refresh()
|
||||
})
|
||||
return () => {
|
||||
offLocale()
|
||||
trigger.dispose()
|
||||
header.dispose()
|
||||
close.dispose()
|
||||
|
||||
@@ -5,19 +5,17 @@
|
||||
* verbatim across locales per the Figma design. Feature-owned rows
|
||||
* (Language, Appearance) ship their copy in their own packages.
|
||||
*/
|
||||
import type { LocaleDict } from '@deepseek-ai/dsh-client-locale/client'
|
||||
import { WELCOME_NOTICE_COPY } from '../onboarding-copy.ts'
|
||||
|
||||
const SHARED = {
|
||||
'permission.value': 'Read only',
|
||||
'toolcall.schema.title': 'Schema mode',
|
||||
'toolcall.schema.desc': 'Traditional function calling — invoke tools one at a time',
|
||||
'toolcall.code.title': 'Code mode',
|
||||
'toolcall.code.desc': 'Chain multiple tools with code — multi-step orchestration',
|
||||
} satisfies LocaleDict
|
||||
} satisfies Record<string, string>
|
||||
|
||||
/** Simplified Chinese dictionary. */
|
||||
export const zh: LocaleDict = {
|
||||
/** Simplified Chinese dictionary (the key-set source of truth). */
|
||||
export const zh = {
|
||||
...SHARED,
|
||||
'trigger': '设置',
|
||||
'title': '设置',
|
||||
@@ -34,10 +32,13 @@ export const zh: LocaleDict = {
|
||||
'welcome.feedbackEmphasis': WELCOME_NOTICE_COPY.zh.feedbackEmphasis,
|
||||
'welcome.continue': WELCOME_NOTICE_COPY.zh.continueLabel,
|
||||
'welcome.error': '暂时无法保存确认状态,请重试。',
|
||||
}
|
||||
} satisfies Record<string, string>
|
||||
|
||||
/** English dictionary. */
|
||||
export const en: LocaleDict = {
|
||||
/** The settings namespace key union. */
|
||||
export type SettingsKey = keyof typeof zh
|
||||
|
||||
/** English dictionary, checked complete against the zh key set. */
|
||||
export const en = {
|
||||
...SHARED,
|
||||
'trigger': 'Settings',
|
||||
'title': 'Settings',
|
||||
@@ -54,4 +55,4 @@ export const en: LocaleDict = {
|
||||
'welcome.feedbackEmphasis': WELCOME_NOTICE_COPY.en.feedbackEmphasis,
|
||||
'welcome.continue': WELCOME_NOTICE_COPY.en.continueLabel,
|
||||
'welcome.error': 'The acknowledgement could not be saved. Please try again.',
|
||||
}
|
||||
} satisfies Record<SettingsKey, string>
|
||||
|
||||
Reference in New Issue
Block a user