feat(apes-sidebar): Phase 0 工程骨架 — host/client 分裂 + i18n + 不变量面
从 0 重写的 v2.0.0 DSH 侧边栏插件第一阶段: - 工程骨架:tsdown 双构建(host ESM + client CJS closure)、vitest、strict TS - host 半:namespace 插件形状(name/inject/apply,无 default),minimal apply - client 半:closure factory 注册 5 个 apes:* tab 占位 + zh/en i18n - 不变量面:assert/semver/peer guard(dsh-better-sidebar >= 0.18.0) - 依赖策略:dsh-better-sidebar=peerDependency(不打包),cordis=devDependency(仅类型) - 门禁:pnpm typecheck + build + test 全绿(14 单测通过) - AGENTS.md 工程约定 + .gitignore(排除 lib/node_modules/maxkb-integration) 模板:dsh-better-sidebar。作用域锁定信息层(tasks/agents/apps/messages/profile), 不含 file/terminal/browser(归 better-sidebar)、不含 team/room(归 background-agents)。
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
/**
|
||||
* Structural types for the cordis services apes-sidebar consumes, plus the
|
||||
* Context face both halves share.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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
|
||||
* 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). */
|
||||
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