refactor: apply repository naming contract
Apply the accepted pre-release package, service, type, directory, and role renames as one repository-wide change.
This commit is contained in:
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write packages/client/locale/README.md
|
||||
README.md: 5bea46cd4e3ace61bd2251610abdf0812ded9604
|
||||
README.zh.md: 2333bc7c2b2f5918c35286064c50131153ee8711
|
||||
README.md: a63807f093dc12831a41196e61008151868e3205
|
||||
README.zh.md: b302e6055ba30d2db0948c0a6f1551c4a9dcb24c
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
English | [中文](README.zh.md)
|
||||
|
||||
Locale plugin: LocaleService — the `zh`/`en` preference stored as `locale.preference` in `$DSH_HOME/settings.yaml`; when that explicit Host value is absent, a fresh browser starts provisionally in the language `navigator` asks for (primary-subtag matching, with `zh` when it asks for no language this app ships). The Host read runs after plugin activation so an unavailable settings service cannot block the page; its result replaces the provisional browser value live. Remote browsers retain only a process-local selection because the settings API is loopback-only. `locale/change` fires on switches. The service also owns the ns×locale dictionary registry (typed `register(ns, {zh, en})` checked against `LocaleNamespaceMap`, `bind(ns)`→`TranslateNS<ns>`; lookup chain ns → common → zh → key), implements the slot system's `LocaleFace`, and installs itself through `ctx.slots.installLocale`, backing the framework-injected `t` standard seat (`Translate`/`TranslateNS` are ui-slots types; import them from there — this package only re-exports for dictionary owners' convenience). The [Host-backed preferences decision](../../../.agents/notes/implemented/bug-fix/2026-08-06-host-backed-web-preferences.md) owns the persistence boundary.
|
||||
Locale plugin: LocaleRuntime — the `zh`/`en` preference stored as `locale.preference` in `$DSH_HOME/settings.yaml`; when that explicit Host value is absent, a fresh browser starts provisionally in the language `navigator` asks for (primary-subtag matching, with `zh` when it asks for no language this app ships). The Host read runs after plugin activation so an unavailable settings service cannot block the page; its result replaces the provisional browser value live. Remote browsers retain only a process-local selection because the settings API is loopback-only. `locale/change` fires on switches. The service also owns the ns×locale dictionary registry (typed `register(ns, {zh, en})` checked against `LocaleNamespaceMap`, `bind(ns)`→`TranslateNS<ns>`; lookup chain ns → common → zh → key), implements the slot system's `LocaleFace`, and installs itself through `ctx.slots.installLocale`, backing the framework-injected `t` standard seat (`Translate`/`TranslateNS` are ui-slots types; import them from there — this package only re-exports for dictionary owners' convenience). The [Host-backed preferences decision](../../../.agents/notes/implemented/bug-fix/2026-08-06-host-backed-web-preferences.md) owns the persistence boundary.
|
||||
|
||||
## Model Experience
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
[English](README.md) | 中文
|
||||
|
||||
locale 插件:LocaleService——`zh`/`en` 偏好以 `locale.preference` 存储在 `$DSH_HOME/settings.yaml` 中;若没有显式 Host 值,全新浏览器会暂时使用 `navigator` 请求的语言(按主子标签匹配;若其请求的语言本应用都不提供,则使用 `zh`)。Host 读取在插件激活后执行,因此 settings 服务不可用不会阻塞页面;读取结果会实时替换浏览器暂定值。settings API 仅限回环请求,因此远程浏览器的选择仅保留在进程内。`locale/change` 仅在切换语言时触发。该服务还拥有 ns×locale 字典注册表(类型化 `register(ns, {zh, en})` 按 `LocaleNamespaceMap` 校验,`bind(ns)`→`TranslateNS<ns>`;查找链 ns → common → zh → key),实现 slot 系统的 `LocaleFace`,并经 `ctx.slots.installLocale` 自行安装,支撑框架注入的 `t` 标准席位(`Translate`/`TranslateNS` 是 ui-slots 的类型;请从那里导入——本包的再导出仅为字典所有者提供便利)。该持久化边界由[Host settings 支撑的偏好决策](../../../.agents/notes/implemented/bug-fix/2026-08-06-host-backed-web-preferences.md)拥有。
|
||||
locale 插件:LocaleRuntime——`zh`/`en` 偏好以 `locale.preference` 存储在 `$DSH_HOME/settings.yaml` 中;若没有显式 Host 值,全新浏览器会暂时使用 `navigator` 请求的语言(按主子标签匹配;若其请求的语言本应用都不提供,则使用 `zh`)。Host 读取在插件激活后执行,因此 settings 服务不可用不会阻塞页面;读取结果会实时替换浏览器暂定值。settings API 仅限回环请求,因此远程浏览器的选择仅保留在进程内。`locale/change` 仅在切换语言时触发。该服务还拥有 ns×locale 字典注册表(类型化 `register(ns, {zh, en})` 按 `LocaleNamespaceMap` 校验,`bind(ns)`→`TranslateNS<ns>`;查找链 ns → common → zh → key),实现 slot 系统的 `LocaleFace`,并经 `ctx.slots.installLocale` 自行安装,支撑框架注入的 `t` 标准席位(`Translate`/`TranslateNS` 是 ui-slots 的类型;请从那里导入——本包的再导出仅为字典所有者提供便利)。该持久化边界由[Host settings 支撑的偏好决策](../../../.agents/notes/implemented/bug-fix/2026-08-06-host-backed-web-preferences.md)拥有。
|
||||
|
||||
## 模型体验
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ export interface LocaleSnapshot {
|
||||
|
||||
declare module '@deepseek-ai/cordis' {
|
||||
interface Context {
|
||||
locale: LocaleService
|
||||
locale: LocaleRuntime
|
||||
}
|
||||
interface Events {
|
||||
/**
|
||||
@@ -111,7 +111,7 @@ const LOCALES: readonly LocaleDefinition[] = Object.freeze([
|
||||
* LocaleFace getSnapshot/subscribe pair the render machinery consumes
|
||||
* (installed via `ctx.slots.installLocale`).
|
||||
*/
|
||||
export class LocaleService {
|
||||
export class LocaleRuntime {
|
||||
private dicts = new Map<string, Map<string, LocaleDict>>()
|
||||
private bound = new Map<string, Translate>()
|
||||
private snapshot: LocaleSnapshot
|
||||
@@ -354,7 +354,7 @@ export const inject = ['slots', 'connection', 'remote', 'settingsScope']
|
||||
*/
|
||||
export function apply(ctx: ClientContext): void {
|
||||
const host = ctx.settingsScope.bind<LocaleSettings>({ namespace: LOCALE_SETTINGS_NAMESPACE })
|
||||
const locale = new LocaleService(ctx, host)
|
||||
const locale = new LocaleRuntime(ctx, host)
|
||||
locale.register(COMMON_NS, { zh, en })
|
||||
locale.register(SETTINGS_NS, { zh: settingsZh, en: settingsEn })
|
||||
ctx.provide('locale', locale)
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
* recovery after an HMR collapse of the declaring entry. */
|
||||
import { Context } from '@deepseek-ai/cordis'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { SlotsService } from '@deepseek-ai/dsh-client-runtime/client'
|
||||
import { SettingsScopeService } from '@deepseek-ai/dsh-client-ui-settings/client'
|
||||
import { SlotRegistry } from '@deepseek-ai/dsh-client-runtime/client'
|
||||
import { SettingsScopeBinder } from '@deepseek-ai/dsh-client-ui-settings/client'
|
||||
import { TestRemote } from '@deepseek-ai/dsh-client-test-runtime'
|
||||
import {
|
||||
apply, inject, SETTINGS_NS,
|
||||
} from '@deepseek-ai/dsh-client-locale/client'
|
||||
import type { LanguageRowInjected, LocaleService } from '@deepseek-ai/dsh-client-locale/client'
|
||||
import type { LanguageRowInjected, LocaleRuntime } from '@deepseek-ai/dsh-client-locale/client'
|
||||
import { LOCALE_SETTINGS_NAMESPACE, LocaleSettingsSchema } from '../src/locale-settings.ts'
|
||||
import { LanguageRow } from '../src/client/LanguageRow.tsx'
|
||||
import type { createLanguageRowStore } from '../src/client/settings-store.ts'
|
||||
@@ -18,7 +18,7 @@ const SLOT = 'settings.general.item'
|
||||
|
||||
async function bench() {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(SlotsService).await()
|
||||
await ctx.plugin(SlotRegistry).await()
|
||||
let preference: string | undefined
|
||||
let revision = 0
|
||||
const namespace = () => ({
|
||||
@@ -47,15 +47,15 @@ async function bench() {
|
||||
ctx.provide('connection', { api: { settings: { describe, mutate } }, isLoopback: true } as never)
|
||||
// The settings transport and the forwarded-event port the plugin injects.
|
||||
new TestRemote(ctx)
|
||||
await ctx.plugin(SettingsScopeService).await()
|
||||
await ctx.plugin(SettingsScopeBinder).await()
|
||||
return {
|
||||
ctx, slots: ctx.get('slots') as SlotsService, describe, mutate,
|
||||
ctx, slots: ctx.get('slots') as SlotRegistry, describe, mutate,
|
||||
setHostPreference: (next: string | undefined) => { preference = next; revision += 1 },
|
||||
}
|
||||
}
|
||||
|
||||
/** Stand in for the settings shell: declare the General item slot from root. */
|
||||
function declareItems(slots: SlotsService): () => void {
|
||||
function declareItems(slots: SlotRegistry): () => void {
|
||||
return slots.register(
|
||||
{ name: 'root', children: { [SLOT]: { kind: 'list', scope: 'root' } } } as never,
|
||||
() => null,
|
||||
@@ -64,7 +64,7 @@ function declareItems(slots: SlotsService): () => void {
|
||||
|
||||
/** Mirror the framework's inject choreography: bake a real instance from the
|
||||
* declared handle and hand its actions to the entry's inject factory. */
|
||||
function faceOf(slots: SlotsService) {
|
||||
function faceOf(slots: SlotRegistry) {
|
||||
const entry = slots.entries(SLOT).find(e => e.component === LanguageRow)!
|
||||
const handle = entry.store as ReturnType<typeof createLanguageRowStore>
|
||||
const instance = handle.create()
|
||||
@@ -91,7 +91,7 @@ describe('locale apply', () => {
|
||||
const before = await bench()
|
||||
declareItems(before.slots)
|
||||
await before.ctx.plugin({ inject: [...inject], apply }).await()
|
||||
const locale = before.ctx.get('locale') as LocaleService
|
||||
const locale = before.ctx.get('locale') as LocaleRuntime
|
||||
// Base dictionaries are registered: the (ns, locale) seats are occupied.
|
||||
expect(() => locale.register('common', 'zh', {})).toThrow('already has locale')
|
||||
expect(() => locale.register('common', 'en', {})).toThrow('already has locale')
|
||||
@@ -112,7 +112,7 @@ describe('locale apply', () => {
|
||||
const b = await bench()
|
||||
declareItems(b.slots)
|
||||
await b.ctx.plugin({ inject: [...inject], apply }).await()
|
||||
const locale = b.ctx.get('locale') as LocaleService
|
||||
const locale = b.ctx.get('locale') as LocaleRuntime
|
||||
// An event ahead of any inject hits the unbound-actions arm.
|
||||
locale.setLocale('en')
|
||||
|
||||
@@ -136,7 +136,7 @@ describe('locale apply', () => {
|
||||
b.setHostPreference('en')
|
||||
declareItems(b.slots)
|
||||
await b.ctx.plugin({ inject: [...inject], apply }).await()
|
||||
const locale = b.ctx.get('locale') as LocaleService
|
||||
const locale = b.ctx.get('locale') as LocaleRuntime
|
||||
await vi.waitFor(() => { expect(locale.getLocale().active).toBe('en') })
|
||||
b.setHostPreference(undefined)
|
||||
b.ctx.remote.$dispatch('settings/document-updated', [LOCALE_SETTINGS_NAMESPACE, 0])
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { Context } from '@deepseek-ai/cordis'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { Settings, settingsNamespace, type SettingsNamespace } from '@deepseek-ai/dsh-settings'
|
||||
import { SettingsProvider, settingsNamespace, type SettingsNamespace } from '@deepseek-ai/dsh-settings'
|
||||
import {
|
||||
LOCALE_SETTINGS_NAMESPACE, apply,
|
||||
} from '@deepseek-ai/dsh-client-locale'
|
||||
|
||||
class MemorySettings extends Settings {
|
||||
class MemorySettings extends SettingsProvider {
|
||||
readonly writable = true
|
||||
protected load(): Promise<Record<string, unknown>> { return Promise.resolve({}) }
|
||||
protected persist(_ns: SettingsNamespace, _section: Record<string, unknown>): Promise<void> {
|
||||
|
||||
@@ -2,16 +2,16 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { Context } from '@deepseek-ai/cordis'
|
||||
import { apply as nodeApply } from '@deepseek-ai/dsh-client-locale'
|
||||
import { apply as clientApply, COMMON_NS, LocaleService, inject } from '@deepseek-ai/dsh-client-locale/client'
|
||||
import { apply as clientApply, COMMON_NS, LocaleRuntime, inject } from '@deepseek-ai/dsh-client-locale/client'
|
||||
import * as LocaleInvariant from '@deepseek-ai/dsh-client-locale/invariant'
|
||||
import { SlotsService } from '@deepseek-ai/dsh-client-runtime/client'
|
||||
import InvariantService from '@deepseek-ai/dsh-invariants'
|
||||
import { SlotRegistry } from '@deepseek-ai/dsh-client-runtime/client'
|
||||
import InvariantRegistry from '@deepseek-ai/dsh-invariants'
|
||||
import { stubSettingsScope } from '@deepseek-ai/dsh-client-test-runtime'
|
||||
|
||||
describe('invariant companion', () => {
|
||||
it('registers under the package name with an empty installer', async () => {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(InvariantService, { enabled: true })
|
||||
await ctx.plugin(InvariantRegistry, { enabled: true })
|
||||
await expect(ctx.plugin(LocaleInvariant).await()).resolves.toBeDefined()
|
||||
})
|
||||
|
||||
@@ -23,16 +23,16 @@ describe('invariant companion', () => {
|
||||
// The feature registers its own Language settings row, hence the slots edge.
|
||||
expect(inject).toEqual(['slots', 'connection', 'remote', 'settingsScope'])
|
||||
const ctx = new Context()
|
||||
new SlotsService(ctx)
|
||||
new SlotRegistry(ctx)
|
||||
ctx.provide('connection', { api: { settings: {} }, isLoopback: false } as never)
|
||||
// The settings row's transport and the forwarded-event port.
|
||||
ctx.provide('remote', { $on: () => () => {} } as never)
|
||||
ctx.provide('settingsScope', { bind: () => stubSettingsScope().scope } as never)
|
||||
await ctx.plugin({ inject, apply: clientApply }).await()
|
||||
const locale = ctx.get('locale')
|
||||
expect(locale).toBeInstanceOf(LocaleService)
|
||||
expect(locale).toBeInstanceOf(LocaleRuntime)
|
||||
// Seeded dictionaries occupy the (ns, locale) seats even while empty.
|
||||
expect(() => (locale as LocaleService).register(COMMON_NS, 'zh', {})).toThrow('already has locale')
|
||||
expect(() => (locale as LocaleService).register(COMMON_NS, 'en', {})).toThrow('already has locale')
|
||||
expect(() => (locale as LocaleRuntime).register(COMMON_NS, 'zh', {})).toThrow('already has locale')
|
||||
expect(() => (locale as LocaleRuntime).register(COMMON_NS, 'en', {})).toThrow('already has locale')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -16,7 +16,7 @@ const OPTIONS = [{ id: 'zh', label: '中文' }, { id: 'en', label: 'English' }]
|
||||
/** Empty global standard-kit hooks (the row reads neither). */
|
||||
function emptySessions() {
|
||||
const store = createSnapshotStore<SessionListState>(
|
||||
{ ids: [], byId: {}, current: undefined, phase: 'ready', subagentsByParent: {}, tasksBySession: {}, currentAddress: undefined })
|
||||
{ ids: [], byId: {}, current: undefined, phase: 'ready', subagentsByParent: {}, jobsBySession: {}, currentAddress: undefined })
|
||||
return bindSnapshotSelector(store)
|
||||
}
|
||||
function emptyWorkspaces() {
|
||||
|
||||
@@ -3,17 +3,17 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { Context } from '@deepseek-ai/cordis'
|
||||
import { stubSettingsScope, type StubSettingsScope } from '@deepseek-ai/dsh-client-test-runtime'
|
||||
import type { LocaleSettings, LocaleSnapshot } from '@deepseek-ai/dsh-client-locale/client'
|
||||
import { LocaleService } from '@deepseek-ai/dsh-client-locale/client'
|
||||
import { LocaleRuntime } from '@deepseek-ai/dsh-client-locale/client'
|
||||
|
||||
const make = (host?: StubSettingsScope<LocaleSettings>): {
|
||||
ctx: Context
|
||||
svc: LocaleService
|
||||
svc: LocaleRuntime
|
||||
events: LocaleSnapshot[]
|
||||
} => {
|
||||
const ctx = new Context()
|
||||
const events: LocaleSnapshot[] = []
|
||||
ctx.on('locale/change', (snapshot) => { events.push(snapshot) })
|
||||
return { ctx, svc: new LocaleService(ctx, host?.scope), events }
|
||||
return { ctx, svc: new LocaleRuntime(ctx, host?.scope), events }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -27,7 +27,7 @@ const stubLanguages = (...tags: string[]): void => {
|
||||
vi.stubGlobal('navigator', { languages: tags, language: tags[0] ?? '' })
|
||||
}
|
||||
|
||||
describe('LocaleService', () => {
|
||||
describe('LocaleRuntime', () => {
|
||||
beforeEach(() => {
|
||||
// A Chinese browser is the baseline these specs assert their zh state on.
|
||||
stubLanguages('zh-CN')
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
"path": "../../settings/settings"
|
||||
},
|
||||
{
|
||||
"path": "../../support/invariants"
|
||||
"path": "../../runtime-diagnostics/invariants"
|
||||
},
|
||||
{
|
||||
"path": "../ui-settings"
|
||||
|
||||
Reference in New Issue
Block a user