feat(apes-sidebar): Phase 1 — 5 个 apes:* tab 真实注册
- client/index.tsx 改用 dsh-better-sidebar 真实 TabDescriptor/ TabComponentProps/Context(type-only,擦除不打包) - 注册 5 个面板:apes:tasks(读 sessions,running/current/subagent 标记)、apes:agents(subagent 过滤 + 按 parent 分组)、apes:apps / apes:messages / apes:profile(占位) - 全部走 t() i18n,ctx.effect 包裹注册(HMR 安全 disposer) - betterSidebar 缺失时优雅降级(bare DSH 不破坏 shell) - context-types.ts 删除被取代的 Phase 0 占位镜像,保留 ApesLocaleService - package.json devDeps 补 dsh-better-sidebar(dev-only 类型) - tests/tabs.spec.ts: mock registry 验证 5 个 apes:* id 注册 + disposer 撤销 + 缺失降级 + namespace 锁 门禁全绿:typecheck / build / test(20/20) cordis scratch 真实挂载冒烟留 Phase 2(需真实 DSH host 进程)
This commit is contained in:
+21
-97
@@ -1,109 +1,33 @@
|
||||
/**
|
||||
* Structural types for the cordis services apes-sidebar consumes, plus the
|
||||
* Context face both halves share.
|
||||
* Structural types for the cordis services apes-sidebar consumes.
|
||||
*
|
||||
* The type base is the `@deepseek-ai/cordis` Context; the service members
|
||||
* this plugin touches are restated below as structural mirrors and combined
|
||||
* with the base by INTERSECTION. Intersection (not `declare module`
|
||||
* augmentation) is deliberate: DSH's own packages already augment
|
||||
* `@deepseek-ai/cordis` and the host and client packages declare *different*
|
||||
* types for the same member — intersecting keeps every face available.
|
||||
* PHASE 1: the sidebar tab + session types now come from the REAL
|
||||
* `dsh-better-sidebar` package (type-only imports in `src/client/index.tsx`):
|
||||
* `TabDescriptor` / `TabComponentProps` / `BetterSidebarService` /
|
||||
* `SidebarSessionSummary` / `SidebarSessionList`. The Phase 0 placeholder
|
||||
* mirrors (`ApesSidebarTabDescriptor`, `ApesSidebarService`,
|
||||
* `ApesSessionSummary`, `ApesSessionList`, `ApesSessionsService`, and the
|
||||
* `ApesContext` intersection) were deleted — they are superseded by the
|
||||
* peer's authoritative types and would drift.
|
||||
*
|
||||
* What remains here is the ONE service face apes-sidebar still defines
|
||||
* itself: the locale runtime mirror (`ApesLocaleService`). `dsh-better-sidebar`
|
||||
* publishes its own `SidebarLocaleService`, but locales.ts pre-dates the type
|
||||
* import and stays on this local mirror to keep the i18n module independent
|
||||
* of the peer (it must compile even when the peer is absent).
|
||||
*
|
||||
* This file must stay FREE of Node.js types: it is part of the
|
||||
* CLIENT-reachable declaration graph (the `Context` in TabComponentProps and
|
||||
* the `apesSidebar` augmentation), so a Node import here would leak into
|
||||
* CLIENT-reachable declaration graph, so a Node import here would leak into
|
||||
* browser-only consumer builds.
|
||||
*/
|
||||
import type { Context as CordisContext } from '@deepseek-ai/cordis'
|
||||
|
||||
/** One session row the sidebar reads (client sessions list feed). */
|
||||
export interface ApesSessionSummary {
|
||||
id: string
|
||||
cwd?: string
|
||||
displayTitle: string
|
||||
/** Coarse durable origin for navigation filtering (subagent children). */
|
||||
origin?: 'subagent'
|
||||
/** Durable direct parent session id (present on subagent children). */
|
||||
parentId?: string
|
||||
/** Whether the session's agent is currently running. */
|
||||
running?: boolean
|
||||
}
|
||||
|
||||
/** The client session list snapshot the sidebar subscribes to. */
|
||||
export interface ApesSessionList {
|
||||
current: string | undefined
|
||||
byId: Record<string, ApesSessionSummary>
|
||||
}
|
||||
|
||||
/** The client sessions service face (only the list feed + open are needed). */
|
||||
export interface ApesSessionsService {
|
||||
list: {
|
||||
getSnapshot(): ApesSessionList
|
||||
subscribe(fn: () => void): () => void
|
||||
}
|
||||
/** Select a listed session as current. */
|
||||
open?(id: string): void
|
||||
}
|
||||
|
||||
/** The client locale service face (mirror of dsh-client-locale's LocaleRuntime). */
|
||||
/**
|
||||
* The client locale service face (mirror of dsh-client-locale's LocaleRuntime).
|
||||
* Only the slices apes-sidebar's i18n module touches: snapshot read, snapshot
|
||||
* subscription, and per-namespace dictionary registration.
|
||||
*/
|
||||
export interface ApesLocaleService {
|
||||
getSnapshot(): { active: string }
|
||||
subscribe(fn: () => void): () => void
|
||||
register(ns: string, locale: string, dict: Record<string, string>): () => void
|
||||
}
|
||||
|
||||
/** The client slots service face (register returns the disposer). */
|
||||
export interface ApesSlotsService {
|
||||
register(options: Record<string, unknown>, component: unknown): () => void
|
||||
inject(key: string, callback: () => () => void): () => void
|
||||
}
|
||||
|
||||
/**
|
||||
* The `ctx.apesSidebar` service face: a registry that lets apes-sidebar
|
||||
* (and future sibling plugins) contribute sidebar tab descriptors to the
|
||||
* better-sidebar host. For Phase 0 this is a minimal self-registry; in
|
||||
* Phase 1 it is re-exported from the better-sidebar `BetterSidebarService`
|
||||
* (type-only import through `dsh-better-sidebar/client/service`).
|
||||
*
|
||||
* NOTE: this is a PLACEHOLDER contract for the skeleton. Phase 1 replaces
|
||||
* it with the real better-sidebar `BetterSidebarService` + `TabDescriptor`
|
||||
* types (type-only imports, which are erased and never reach the bundle
|
||||
* purity gate).
|
||||
*/
|
||||
export interface ApesSidebarTabDescriptor {
|
||||
/** Unique id; also the `SidebarTab.type` value (`'apes:tasks'`, …). */
|
||||
id: string
|
||||
title: string | (() => string)
|
||||
icon?: unknown
|
||||
/** + menu sort order (ascending); default 100. */
|
||||
order?: number
|
||||
hidden?: boolean
|
||||
/** Single-instance sugar. */
|
||||
single?: boolean
|
||||
/** Tab-strip badge: a number renders as a count, a string as-is. */
|
||||
badge?: (ctx: ApesContext, state: unknown) => string | number | null | undefined
|
||||
component: (props: Record<string, unknown>) => unknown
|
||||
}
|
||||
|
||||
/** The `ctx.apesSidebar` service face (Phase 0 placeholder). */
|
||||
export interface ApesSidebarService {
|
||||
registerTab(descriptor: ApesSidebarTabDescriptor): () => void
|
||||
getTabs(): readonly ApesSidebarTabDescriptor[]
|
||||
subscribe(listener: () => void): () => void
|
||||
}
|
||||
|
||||
/**
|
||||
* The Context face this plugin's code resolves against. Intersecting the
|
||||
* cordis base with the structural service mirrors keeps every face
|
||||
* available without module-level conflicts.
|
||||
*/
|
||||
export type ApesContext = CordisContext & {
|
||||
/** The client-side registry (client half only). */
|
||||
apesSidebar?: ApesSidebarService
|
||||
/** The client sessions feed (client half only). */
|
||||
sessions?: ApesSessionsService
|
||||
/** The client locale runtime (client half only). */
|
||||
locale?: ApesLocaleService
|
||||
/** The client slots service (client half only). */
|
||||
slots?: ApesSlotsService
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user