mirror of
https://github.com/deepseek-ai/deepseek-harness
synced 2026-08-15 21:04:50 +00:00
Merge remote-tracking branch 'origin/master' into worktree/i18n-complete-non-readme
# Conflicts: # packages/client/i18n/README.md # packages/client/locale/README.i18n.yaml # packages/client/locale/README.zh.md # packages/client/ui-layout/README.md # packages/client/ui-theme/README.md
This commit is contained in:
6
packages/client/ui-models/README.i18n.yaml
Normal file
6
packages/client/ui-models/README.i18n.yaml
Normal file
@@ -0,0 +1,6 @@
|
||||
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
|
||||
# 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
|
||||
README.md: 13f51d5338affd65d0705cec6a3b4ef78a534f0f
|
||||
README.zh.md: 466505beb27c729246afe04e6378235b91d072cf
|
||||
17
packages/client/ui-models/README.md
Normal file
17
packages/client/ui-models/README.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# @deepseek-ai/dsh-client-ui-models
|
||||
|
||||
English | [中文](README.zh.md)
|
||||
|
||||
Models settings section plugin: registers the `models` nav entry into `settings.section` with an intentionally empty content column — model management lands in a later phase.
|
||||
|
||||
## Model Experience
|
||||
|
||||
None, as the section renders an empty browser UI column; nothing here reaches a model request.
|
||||
|
||||
#### KV Cache effect
|
||||
|
||||
None; this package neither assembles nor sends a provider request.
|
||||
|
||||
## Known Limitations and Deferred Work
|
||||
|
||||
- **Content column is empty by design** — provider list, editing form, and activation flow are deferred until the model-management service exists.
|
||||
17
packages/client/ui-models/README.zh.md
Normal file
17
packages/client/ui-models/README.zh.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# @deepseek-ai/dsh-client-ui-models
|
||||
|
||||
[English](README.md) | 中文
|
||||
|
||||
模型设置分区插件:注册 `models` 导航项,使其进入 `settings.section`;内容栏有意留空,模型管理将在后续阶段实现。
|
||||
|
||||
## 模型体验
|
||||
|
||||
无。该分区渲染空白的浏览器 UI 内容栏;这里没有任何内容进入模型请求。
|
||||
|
||||
#### KV Cache 影响
|
||||
|
||||
无;该包既不组装也不发送提供方请求。
|
||||
|
||||
## 已知限制与暂缓事项
|
||||
|
||||
- **内容栏按设计留空**:提供方列表、编辑表单和激活流程均暂缓,待模型管理服务就绪后实现。
|
||||
63
packages/client/ui-models/package.json
Normal file
63
packages/client/ui-models/package.json
Normal file
@@ -0,0 +1,63 @@
|
||||
{
|
||||
"name": "@deepseek-ai/dsh-client-ui-models",
|
||||
"description": "Models feature plugin: registers its Settings section (nav entry, empty content column; model management lands later)",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "lib/index.js",
|
||||
"types": "lib/types/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./lib/types/index.d.ts",
|
||||
"default": "./lib/index.js"
|
||||
},
|
||||
"./invariant": {
|
||||
"types": "./lib/types/invariant.d.ts",
|
||||
"default": "./lib/invariant.js"
|
||||
},
|
||||
"./client": {
|
||||
"types": "./lib/types/client/index.d.ts",
|
||||
"default": "./lib/client.js"
|
||||
},
|
||||
"./src/*": "./src/*",
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"dshClient": {
|
||||
"inject": [
|
||||
"@deepseek-ai/dsh-client-runtime",
|
||||
"@deepseek-ai/dsh-client-ui-settings",
|
||||
"@deepseek-ai/dsh-client-locale"
|
||||
],
|
||||
"platform": "web"
|
||||
},
|
||||
"scripts": {
|
||||
"bundle": "tsdown",
|
||||
"watch": "tsdown --watch"
|
||||
},
|
||||
"license": "BSD-3-Clause",
|
||||
"peerDependencies": {
|
||||
"@deepseek-ai/dsh-client-runtime": "^0.0.1",
|
||||
"@deepseek-ai/dsh-client-ui-slots": "^0.0.1",
|
||||
"@deepseek-ai/dsh-invariants": "^0.0.1",
|
||||
"cordis": "^4.0.0-rc.7",
|
||||
"react": "^18.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@deepseek-ai/dsh-client-locale": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-runtime": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-settings": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-slots": "workspace:^",
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^",
|
||||
"@types/react": "~18.3.1",
|
||||
"cordis": "^4.0.0-rc.7",
|
||||
"react": "^18.2.0"
|
||||
},
|
||||
"files": [
|
||||
"lib/index.js",
|
||||
"lib/invariant.js",
|
||||
"lib/client.js",
|
||||
"lib/types/**/*.d.ts",
|
||||
"lib/types/**/*.d.ts.map",
|
||||
"src"
|
||||
]
|
||||
}
|
||||
13
packages/client/ui-models/src/client/ModelsSection.tsx
Normal file
13
packages/client/ui-models/src/client/ModelsSection.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Models settings section: an intentionally empty content column — the nav
|
||||
* entry exists so the section slot composition is visible; model management
|
||||
* lands in a later phase.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Render the (empty) Models section content column.
|
||||
* @returns null — no content this phase.
|
||||
*/
|
||||
export function ModelsSection() {
|
||||
return null
|
||||
}
|
||||
51
packages/client/ui-models/src/client/index.ts
Normal file
51
packages/client/ui-models/src/client/index.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* Models settings section plugin, browser half. Registers the `models` nav
|
||||
* entry into the shell-declared `settings.section` list slot; the content
|
||||
* column is intentionally empty until model management lands. Export
|
||||
* discipline: packages/client/AGENTS.md.
|
||||
*/
|
||||
import type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client'
|
||||
import { deferRegistration } 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'
|
||||
// Type-only: pulls the locale plugin's Context merge (ctx.locale).
|
||||
import type {} from '@deepseek-ai/dsh-client-locale/client'
|
||||
import { ModelsSection } from './ModelsSection.tsx'
|
||||
|
||||
/**
|
||||
* Required services (cordis fiber inject). The target slot is declared by
|
||||
* ui-settings' apply, whose activation order relative to this one is NOT
|
||||
* constrained; registration goes through declaration-aware deferral.
|
||||
*/
|
||||
export const inject = ['slots', 'locale']
|
||||
|
||||
/**
|
||||
* Register the Models section once the `settings.section` declaration is on
|
||||
* the ledger.
|
||||
* @param ctx - client root context.
|
||||
*/
|
||||
export function apply(ctx: ClientContext): void {
|
||||
ctx.effect(() => {
|
||||
const disposers = [
|
||||
ctx.locale.register('settings.models', 'zh', { nav: '模型' }),
|
||||
ctx.locale.register('settings.models', 'en', { nav: 'Models' }),
|
||||
]
|
||||
return () => { for (const dispose of disposers) dispose() }
|
||||
}, 'ui-models: nav copy dictionaries')
|
||||
ctx.effect(() => {
|
||||
const deferred = deferRegistration(ctx.slots, 'settings.section', ModelsSection, () =>
|
||||
ctx.slots.register({
|
||||
name: 'settings.section',
|
||||
id: 'models',
|
||||
order: 10,
|
||||
label: ctx.locale.bind('settings.models')('nav'),
|
||||
}, ModelsSection))
|
||||
// Nav labels are registrant-localized: refresh on locale change so the
|
||||
// ledger carries fresh text (the version bump re-renders the shell).
|
||||
const offLocale = ctx.on('locale/change', () => { deferred.refresh() })
|
||||
return () => {
|
||||
offLocale()
|
||||
deferred.dispose()
|
||||
}
|
||||
}, 'ui-models: settings section registration')
|
||||
}
|
||||
6
packages/client/ui-models/src/css-modules.d.ts
vendored
Normal file
6
packages/client/ui-models/src/css-modules.d.ts
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
declare module '*.module.css' {
|
||||
const classes: Record<string, string>
|
||||
export default classes
|
||||
}
|
||||
|
||||
declare module '*.css'
|
||||
4
packages/client/ui-models/src/index.ts
Normal file
4
packages/client/ui-models/src/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
/** Host loader entry for the browser implementation exported from `./client`. */
|
||||
|
||||
/** Host plugin body — no host-side behavior for the models settings plugin. */
|
||||
export function apply(): void {}
|
||||
31
packages/client/ui-models/src/invariant.ts
Normal file
31
packages/client/ui-models/src/invariant.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* Package-owned invariant companion for `@deepseek-ai/dsh-client-ui-models`.
|
||||
* @module @deepseek-ai/dsh-client-ui-models/invariant
|
||||
*/
|
||||
|
||||
/* jscpd:ignore-start */
|
||||
import type { Context } from 'cordis'
|
||||
import type { InvariantInstaller } from '@deepseek-ai/dsh-invariants'
|
||||
|
||||
const PACKAGE_NAME = '@deepseek-ai/dsh-client-ui-models'
|
||||
|
||||
/** Cordis companion plugin name. */
|
||||
export const name = 'client-ui-models-invariant'
|
||||
/** Service required before the companion can reserve package ownership. */
|
||||
export const inject = ['invariants']
|
||||
|
||||
/**
|
||||
* No runtime invariant: a nav-entry-only section plugin rendering a fixed
|
||||
* empty content column — it emits no cordis events and owns no cross-plugin
|
||||
* mutable relation.
|
||||
*/
|
||||
const install: InvariantInstaller = () => {}
|
||||
|
||||
/**
|
||||
* Register this package's invariant companion.
|
||||
* @param ctx - Cordis context carrying the invariant service.
|
||||
* @returns the installed registration's disposer after setup succeeds.
|
||||
*/
|
||||
export const apply = (ctx: Context): Promise<() => void> =>
|
||||
Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install))
|
||||
/* jscpd:ignore-end */
|
||||
95
packages/client/ui-models/tests/apply.spec.ts
Normal file
95
packages/client/ui-models/tests/apply.spec.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
/** Models section registration: declaration-aware deferral, locale re-registration, and HMR recovery. */
|
||||
import { Context } from 'cordis'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { SlotsService } from '@deepseek-ai/dsh-client-runtime/client'
|
||||
import { LocaleService } from '@deepseek-ai/dsh-client-locale/client'
|
||||
import { apply, inject } from '@deepseek-ai/dsh-client-ui-models/client'
|
||||
import { ModelsSection } from '../src/client/ModelsSection.tsx'
|
||||
|
||||
async function bench() {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(SlotsService).await()
|
||||
const locale = new LocaleService(ctx)
|
||||
ctx.provide('locale', locale)
|
||||
return { ctx, slots: ctx.get('slots') as SlotsService, locale }
|
||||
}
|
||||
|
||||
function declare(slots: SlotsService): () => void {
|
||||
return slots.register(
|
||||
{ name: 'root', children: { 'settings.section': { kind: 'list', scope: 'root' } } } as never,
|
||||
() => null,
|
||||
)
|
||||
}
|
||||
|
||||
describe('ui-models apply', () => {
|
||||
it('declares the services it uses', () => {
|
||||
expect(inject).toEqual(['slots', 'locale'])
|
||||
})
|
||||
|
||||
it('registers the models nav entry for declarations before or after apply', async () => {
|
||||
const before = await bench()
|
||||
declare(before.slots)
|
||||
await before.ctx.plugin({ inject: [...inject], apply }).await()
|
||||
const entry = before.slots.entries('settings.section')[0]!
|
||||
expect(entry.component).toBe(ModelsSection)
|
||||
expect(entry.options).toEqual({ id: 'models', order: 10, label: '模型' })
|
||||
|
||||
const after = await bench()
|
||||
await after.ctx.plugin({ inject: [...inject], apply }).await()
|
||||
expect(after.slots.entries('settings.section')).toHaveLength(0)
|
||||
declare(after.slots)
|
||||
await Promise.resolve()
|
||||
expect(after.slots.entries('settings.section')[0]!.component).toBe(ModelsSection)
|
||||
// The self-inflicted ledger notifications hit the duplicate guard.
|
||||
expect(after.slots.entries('settings.section')).toHaveLength(1)
|
||||
})
|
||||
|
||||
it('re-registers with fresh label text on locale change', async () => {
|
||||
const b = await bench()
|
||||
declare(b.slots)
|
||||
await b.ctx.plugin({ inject: [...inject], apply }).await()
|
||||
b.locale.setLocale('en')
|
||||
expect(b.slots.entries('settings.section')[0]!.options.label).toBe('Models')
|
||||
b.locale.setLocale('zh')
|
||||
expect(b.slots.entries('settings.section')[0]!.options.label).toBe('模型')
|
||||
})
|
||||
|
||||
it('locale change while the slot is undeclared stays a no-op', async () => {
|
||||
const b = await bench()
|
||||
await b.ctx.plugin({ inject: [...inject], apply }).await()
|
||||
b.locale.setLocale('en')
|
||||
expect(b.slots.entries('settings.section')).toHaveLength(0)
|
||||
b.locale.setLocale('zh')
|
||||
})
|
||||
|
||||
it('re-registers after an HMR collapse re-declares the slot (stale disposer must not block)', async () => {
|
||||
const b = await bench()
|
||||
const redeclare = declare(b.slots)
|
||||
await b.ctx.plugin({ inject: [...inject], apply }).await()
|
||||
expect(b.slots.entries('settings.section')).toHaveLength(1)
|
||||
// Declarer unload: the cascade removes our entry while our local
|
||||
// disposer variable goes stale.
|
||||
redeclare()
|
||||
expect(b.slots.entries('settings.section')).toHaveLength(0)
|
||||
declare(b.slots)
|
||||
await Promise.resolve()
|
||||
expect(b.slots.entries('settings.section')[0]!.component).toBe(ModelsSection)
|
||||
// The locale path also recovers through the same ledger re-check.
|
||||
b.locale.setLocale('en')
|
||||
expect(b.slots.entries('settings.section')[0]!.options.label).toBe('Models')
|
||||
b.locale.setLocale('zh')
|
||||
})
|
||||
|
||||
it('registers the zh/en nav dictionaries and disposes everything with the fiber', async () => {
|
||||
const b = await bench()
|
||||
declare(b.slots)
|
||||
const fiber = b.ctx.plugin({ inject: [...inject], apply })
|
||||
await fiber.await()
|
||||
expect(b.locale.bind('settings.models')('nav')).toBe('模型')
|
||||
await fiber.dispose()
|
||||
expect(b.slots.entries('settings.section')).toHaveLength(0)
|
||||
// The (ns, locale) seats are free again — the dictionary disposers ran.
|
||||
expect(() => b.locale.register('settings.models', 'zh', {})).not.toThrow()
|
||||
expect(() => b.locale.register('settings.models', 'en', {})).not.toThrow()
|
||||
})
|
||||
})
|
||||
23
packages/client/ui-models/tests/invariant.spec.ts
Normal file
23
packages/client/ui-models/tests/invariant.spec.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { Context } from 'cordis'
|
||||
import * as ModelsInvariant from '@deepseek-ai/dsh-client-ui-models/invariant'
|
||||
import InvariantService from '@deepseek-ai/dsh-invariants'
|
||||
import { ModelsSection } from '../src/client/ModelsSection.tsx'
|
||||
|
||||
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 expect(ctx.plugin(ModelsInvariant).await()).resolves.toBeDefined()
|
||||
})
|
||||
|
||||
it('node-half apply is a no-op host placeholder', async () => {
|
||||
const { apply } = await import('@deepseek-ai/dsh-client-ui-models')
|
||||
apply()
|
||||
expect(true).toBe(true) // reaching here without throw is the contract
|
||||
})
|
||||
|
||||
it('the section content column is intentionally empty this phase', () => {
|
||||
expect(ModelsSection()).toBeNull()
|
||||
})
|
||||
})
|
||||
30
packages/client/ui-models/tsconfig.json
Normal file
30
packages/client/ui-models/tsconfig.json
Normal file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.base.client.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "src",
|
||||
"outDir": "lib/types"
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"path": "../../../vendor/cordis"
|
||||
},
|
||||
{
|
||||
"path": "../ui-slots"
|
||||
},
|
||||
{
|
||||
"path": "../runtime"
|
||||
},
|
||||
{
|
||||
"path": "../ui-settings"
|
||||
},
|
||||
{
|
||||
"path": "../locale"
|
||||
},
|
||||
{
|
||||
"path": "../../support/invariants"
|
||||
}
|
||||
]
|
||||
}
|
||||
3
packages/client/ui-models/tsdown.config.ts
Normal file
3
packages/client/ui-models/tsdown.config.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import { clientBundle } from '../tsdown.client.ts'
|
||||
|
||||
export default clientBundle('@deepseek-ai/dsh-client-ui-models', ['lib/types/index.js', 'lib/types/invariant.js'])
|
||||
Reference in New Issue
Block a user