feat(v1.1.0): adapt to DSH 2.0.10 (0.1.5-rc.2), drop apes:tasks, DSW token styles

C1-C5 env repair:
- align all @deepseek-ai/dsh-* peer+devDeps to 0.1.5-rc.2 (DSH 2.0.10 ships it)
- fix 0.1.5-rc.2 npm packaging defect: 15 primitives runtime deps (anser,
  shiki, katex, marked, ... ) were mis-declared as devDeps in the published
  package -> add to local devDependencies so vitest can resolve them
- fix sidechat-seed contract: dsh-session@0.1.5 assertAssistantSettlementShape
  now requires a top-level data.stream: Array on assistant messages -> seed
  helper emits stream: []

C6 slim tabs:
- remove apes:tasks (overlaps the official DSH session tree in the native
  sidebar); keep apes:agents / apes:apps / apes:profile
- clean dead code: ApesTasksView + job helpers import + apes i18n dead keys
  (tasks/status/duration) and the unused menu-title keys

C7 style align:
- apes view inline colors -> DSW alias tokens (--dsw-alias-bg/border/label/
  state-success/interactive) with literal fallbacks; geometry (font/spacing/
  radius) stays inline (DSH exposes no --ds-* geometry tokens to plugin panels)

gate: typecheck ok / build ok / test 119 files · 1249 passed
This commit is contained in:
figmar
2026-09-16 01:57:46 +08:00
parent 34e86141bd
commit aa28c1fbc7
8 changed files with 526 additions and 792 deletions
+20 -31
View File
@@ -1,10 +1,12 @@
/**
* apes:* tab registration tests (Phase 1/1b): the fork registers its four
* information-layer tabs (tasks / agents / apps / profile) through the same
* BetterSidebarService the upstream builtins use (dogfood). The spec pins
* the registration contract (id / single / order / component presence) and
* the isolated i18n contract (the apes namespace resolves zh vs en through
* the attached locale service, and every apesZh key has an apesEn entry).
* apes:* tab registration tests (Phase 1/1b, C6: 3 tabs). The fork registers
* its three information-layer tabs (agents / apps / profile) through the same
* BetterSidebarService the upstream builtins use (dogfood). `apes:tasks` was
* removed in C6 — it overlaps the official DSH session tree in the native
* sidebar. The spec pins the registration contract (id / single / order /
* component presence) and the isolated i18n contract (the apes namespace
* resolves zh vs en through the attached locale service, and every apesZh
* key has an apesEn entry).
*
* Runs in the node vitest environment like builtins.spec.ts — no DOM, no
* real DSH host (a live cordis scratch mount is Phase 2+).
@@ -31,11 +33,11 @@ function setup(): {
}
describe('apes:* tab registrations', () => {
it('registers the 4 apes tabs with the apes: namespace', () => {
it('registers the 3 apes tabs with the apes: namespace', () => {
const { service, dispose } = setup()
try {
expect(service.getTabs().map(t => t.id).sort()).toEqual(
['apes:agents', 'apes:apps', 'apes:profile', 'apes:tasks'],
['apes:agents', 'apes:apps', 'apes:profile'],
)
} finally {
dispose()
@@ -45,7 +47,7 @@ describe('apes:* tab registrations', () => {
it('every apes tab is single-instance, visible, and component-carrying', () => {
const { service, dispose } = setup()
try {
for (const id of ['apes:tasks', 'apes:agents', 'apes:apps', 'apes:profile']) {
for (const id of ['apes:agents', 'apes:apps', 'apes:profile']) {
const tab = service.getTab(id)
expect(tab, `${id} registered`).toBeDefined()
expect(tab?.single, `${id} single`).toBe(true)
@@ -62,17 +64,14 @@ describe('apes:* tab registrations', () => {
const { service, dispose } = setup()
try {
const orders = {
tasks: service.getTab('apes:tasks')?.order,
agents: service.getTab('apes:agents')?.order,
apps: service.getTab('apes:apps')?.order,
profile: service.getTab('apes:profile')?.order,
}
expect(orders.tasks).toBeGreaterThanOrEqual(900)
expect(orders.agents).toBeGreaterThanOrEqual(900)
expect(orders.apps).toBeGreaterThanOrEqual(900)
expect(orders.profile).toBeGreaterThanOrEqual(900)
// Strictly ordered: tasks < agents < apps < profile.
expect(orders.tasks!).toBeLessThan(orders.agents!)
// Strictly ordered: agents < apps < profile.
expect(orders.agents!).toBeLessThan(orders.apps!)
expect(orders.apps!).toBeLessThan(orders.profile!)
} finally {
@@ -84,7 +83,7 @@ describe('apes:* tab registrations', () => {
const store = createSidebarStore()
const service = createBetterSidebarService(store)
const dispose = registerApesTabs({} as Context, service)
expect(service.getTabs()).toHaveLength(4)
expect(service.getTabs()).toHaveLength(3)
dispose()
expect(service.getTabs()).toHaveLength(0)
})
@@ -102,30 +101,20 @@ describe('apes i18n contract', () => {
it('resolves zh when the DSH preference is zh', () => {
attachApesLocale({ getSnapshot: () => ({ active: 'zh-CN' }) })
expect(t('apesTasks')).toBe('任务')
expect(t('apesAgents')).toBe('智能体')
expect(t('apesApps')).toBe('应用')
expect(t('apesProfile')).toBe('画像')
expect(t('apesAgentsTitle')).toBe('智能体')
expect(t('apesAppsTitle')).toBe('应用')
expect(t('apesProfileTitle')).toBe('画像')
})
it('resolves en when the DSH preference is en', () => {
attachApesLocale({ getSnapshot: () => ({ active: 'en' }) })
expect(t('apesTasks')).toBe('Tasks')
expect(t('apesAgents')).toBe('Agents')
expect(t('apesApps')).toBe('Apps')
expect(t('apesProfile')).toBe('Profile')
expect(t('apesAgentsTitle')).toBe('Agents')
expect(t('apesAppsTitle')).toBe('Apps')
expect(t('apesProfileTitle')).toBe('Profile')
})
it('falls back to zh when no locale service is attached', () => {
attachApesLocale(undefined)
expect(t('apesTasks')).toBe('任务')
})
it('supports {param} interpolation', () => {
attachApesLocale({ getSnapshot: () => ({ active: 'zh' }) })
// The duration strings carry {seconds} params — assert the mechanism via
// a real key (the t() contract, not a synthetic one).
expect(t('apesDurationSeconds', { seconds: 5 })).toBe('5 秒')
expect(t('apesDurationMinutes', { minutes: 2, seconds: 7 })).toBe('2 分 7 秒')
expect(t('apesAgentsTitle')).toBe('智能体')
})
})
+1 -1
View File
@@ -56,7 +56,7 @@ function parentLog(): SidebarSessionEvent[] {
ev('user/message', 0, userMessage('first question')),
ev('turn/start', 1, { turn: 1 }),
ev('step/start', 2, { turn: 1, step: 1 }),
ev('assistant/message', 3, { turn: 1, step: 1, message: assistantMessage('first answer') }),
ev('assistant/message', 3, { turn: 1, step: 1, stream: [], message: assistantMessage('first answer') }),
ev('step/end', 4, { turn: 1, step: 1 }),
ev('turn/end', 5, { turn: 1, reason: { kind: 'completed' } }),
ev('user/message', 6, userMessage('pending question')),