dsh-brief-sidebar 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","names":["STATUS_KEY","NO_SUBSCRIBE","ABSENT","useMemo","useCallback","useSyncExternalStore","useMemo","SECTION_STYLE","HEADER_STYLE","LIST_STYLE","ROW_STYLE","createElement","SECTION_STYLE","HEADER_STYLE","LIST_STYLE","ROW_STYLE","CHEVRON_STYLE","createElement","useState","HEADER_STYLE","ROW_STYLE","BODY_STYLE","createElement","NO_SUBSCRIBE","useMemo","useCallback","useSyncExternalStore","createElement","useState","useSyncExternalStore","useCallback","createElement","createElement"],"sources":["../src/client/brief/board.ts","../src/client/use-projection.ts","../src/client/brief/use-todos.ts","../src/client/TodoBoardTab.tsx","../src/projection/keys.ts","../src/client/file-open.ts","../src/client/summary/deliverables.ts","../src/client/locales.ts","../src/client/summary/DeliverablesSection.tsx","../src/client/summary/memory-slots.ts","../src/client/scope-face.ts","../src/client/summary/MemorySlotsSection.tsx","../src/client/SummaryTab.tsx","../src/client/BriefIcon.tsx","../src/client/brief/dock-shadow.tsx","../src/client/settings-card.tsx","../src/client/index.tsx"],"sourcesContent":["/**\r\n * The `todos` projection — reading and classifying it.\r\n *\r\n * The Host computes this projection (`dsh-tool-todo` registers the key) and\r\n * pushes finished whole values; the client never folds it. This module is the\r\n * read side of that contract and is deliberately PURE — no React, no context,\r\n * no IO — so the mapping can be tested directly.\r\n */\r\n/** The projection's three-state lifecycle (`TodoItem` in `dsh-tool-todo`). */\r\nexport type TodoStatus = 'pending' | 'in_progress' | 'completed'\r\n\r\n/** One entry of the `todos` projection. */\r\nexport interface TodoItem {\r\n readonly content: string\r\n readonly status: TodoStatus\r\n}\r\n\r\nconst STATUSES: readonly TodoStatus[] = ['pending', 'in_progress', 'completed']\r\n\r\n/** Locale key per status. Kept here so this module owns the vocabulary. */\r\nconst STATUS_KEY: Record<TodoStatus, string> = {\r\n pending: 'status.pending',\r\n in_progress: 'status.inProgress',\r\n completed: 'status.completed',\r\n}\r\n\r\n/** Locale key for one status label. */\r\nexport function statusKey(status: TodoStatus): string {\r\n return STATUS_KEY[status]\r\n}\r\n\r\n/**\r\n * Narrow a raw projection value into a todo list.\r\n *\r\n * Three outcomes are kept apart on purpose, mirroring the store's own contract:\r\n * - `undefined` — the capability is ABSENT (no session, the host unit is\r\n * unmounted, or no baseline/frame has carried the key yet);\r\n * - `[]` — the projection is present and the list is empty (`null` on the wire);\r\n * - a list — real entries.\r\n *\r\n * A malformed entry is dropped rather than thrown on: the projection is a\r\n * trusted Host value, but a shape drift must not blank the whole board.\r\n *\r\n * @param value - the raw `unknown` snapshot of the `todos` face.\r\n * @returns the validated list, or `undefined` when the capability is absent.\r\n */\r\nexport function readTodos(value: unknown): readonly TodoItem[] | undefined {\r\n if (value === null) return []\r\n if (!Array.isArray(value)) return undefined\r\n\r\n const items: TodoItem[] = []\r\n for (const raw of value) {\r\n if (raw === null || typeof raw !== 'object') continue\r\n const { content, status } = raw as { content?: unknown; status?: unknown }\r\n if (typeof content !== 'string') continue\r\n if (typeof status !== 'string' || !isStatus(status)) continue\r\n items.push({ content, status })\r\n }\r\n return items\r\n}\r\n\r\n/** Whether a string is one of the three lifecycle values. */\r\nfunction isStatus(value: string): value is TodoStatus {\r\n return (STATUSES as readonly string[]).includes(value)\r\n}\r\n\r\n/** Per-status counts over one list. */\r\nexport interface TodoCounts {\r\n readonly total: number\r\n readonly completed: number\r\n readonly inProgress: number\r\n readonly pending: number\r\n}\r\n\r\n/** Count a todo list by status. */\r\nexport function countTodos(todos: readonly TodoItem[]): TodoCounts {\r\n let completed = 0\r\n let inProgress = 0\r\n let pending = 0\r\n for (const item of todos) {\r\n if (item.status === 'completed') completed += 1\r\n else if (item.status === 'in_progress') inProgress += 1\r\n else pending += 1\r\n }\r\n return { total: todos.length, completed, inProgress, pending }\r\n}\r\n\r\n/** How many entries are not finished — the tab badge. */\r\nexport function unfinishedCount(todos: readonly TodoItem[]): number {\r\n const counts = countTodos(todos)\r\n return counts.inProgress + counts.pending\r\n}\r\n\r\n/**\r\n * The one-line progress summary, composed from parts so no interpolation\r\n * machinery is needed (the locale binder is a plain key → string lookup).\r\n * Mirrors the official dock's own label.\r\n */\r\nexport function progressLine(counts: TodoCounts, t: (key: string) => string): string {\r\n const parts = [`${t('status.completed')} ${counts.completed}/${counts.total}`]\r\n if (counts.inProgress > 0) parts.push(`${t('status.inProgress')} ${counts.inProgress}`)\r\n if (counts.pending > 0) parts.push(`${t('status.pending')} ${counts.pending}`)\r\n return parts.join(' · ')\r\n}\r\n","/**\r\n * Reading a live session projection from the client Session Controller.\r\n *\r\n * better-sidebar tab bodies are NOT rendered inside a DSH slot, so the\r\n * framework's `useProjection` standard prop is not available to them. The\r\n * equivalent face is reachable directly: the client Session Controller is a\r\n * cordis service on the root context, and each session binding exposes its\r\n * projections store (`SessionFace.projections.faceOf(key)`), which is the very\r\n * observable `useProjection` resolves. Since it is a bare\r\n * `{ getSnapshot, subscribe }` source, `useSyncExternalStore` binds it as-is —\r\n * no local mirror, no folding, and the value reference only changes when the\r\n * Host pushes a frame.\r\n *\r\n * Everything here is resolved defensively: a host without the Session\r\n * Controller, or a session that is not open any more, degrades to \"capability\r\n * absent\" (undefined) rather than throwing inside a render.\r\n *\r\n * This module is key-agnostic; the per-projection knowledge (which key, how to\r\n * narrow the raw value) lives with the feature modules that call it.\r\n */\r\nimport { useCallback, useMemo, useSyncExternalStore } from 'react'\r\nimport type { Context } from '@deepseek-ai/cordis'\r\n\r\n/** Structural view of a projection face (`ObservableSnapshot<unknown>`). */\r\nexport interface ProjectionFace {\r\n getSnapshot(): unknown\r\n subscribe(listener: () => void): () => void\r\n}\r\n\r\n/** Structural view of one session binding's projection store. */\r\nexport interface ProjectionsLike {\r\n faceOf(key: string): ProjectionFace\r\n}\r\n\r\n/** Structural view of the client Session Controller face this plugin uses. */\r\nexport interface SessionsLike {\r\n binding(id: string): { readonly session: { readonly projections: ProjectionsLike } } | undefined\r\n}\r\n\r\n/** A no-op unsubscribe, kept as one identity so effects never thrash. */\r\nconst NO_SUBSCRIBE = (): void => {}\r\n\r\n/** A source that never has a value — the absent-capability face. */\r\nconst ABSENT: ProjectionFace = { getSnapshot: () => undefined, subscribe: () => NO_SUBSCRIBE }\r\n\r\n/**\r\n * Whether a value really is a projection face.\r\n *\r\n * The store contract promises an identity-stable face for every key, so this\r\n * never fires against a healthy host. It exists because the fallback is worse\r\n * than the check: `useSyncExternalStore` would throw on a face-less key, and a\r\n * thrown render is a blank sidebar rather than a diagnosable \"unavailable\".\r\n */\r\nfunction isFace(value: unknown): value is ProjectionFace {\r\n if (value === null || typeof value !== 'object') return false\r\n const face = value as Partial<ProjectionFace>\r\n return typeof face.getSnapshot === 'function' && typeof face.subscribe === 'function'\r\n}\r\n\r\n/**\r\n * Resolve one projection face for one session.\r\n *\r\n * `ctx.get('sessions')` is the safe accessor (a direct `ctx.sessions` read can\r\n * throw on a context that does not carry the service yet).\r\n *\r\n * @param ctx - the client root context handed to the tab body.\r\n * @param sessionId - the tab's session scope; undefined means \"no session\".\r\n * @param key - the projection key to resolve.\r\n * @returns the projection face, or {@link ABSENT} when unreachable.\r\n */\r\nexport function resolveFace(ctx: Context | undefined, sessionId: string | undefined, key: string): ProjectionFace {\r\n if (ctx === undefined || sessionId === undefined) return ABSENT\r\n const sessions = ctx.get('sessions') as SessionsLike | undefined\r\n if (sessions === undefined || typeof sessions.binding !== 'function') return ABSENT\r\n\r\n const binding = sessions.binding(sessionId)\r\n const projections = binding?.session?.projections\r\n if (projections === undefined || typeof projections.faceOf !== 'function') return ABSENT\r\n\r\n const face: unknown = projections.faceOf(key)\r\n return isFace(face) ? face : ABSENT\r\n}\r\n\r\n/**\r\n * Read one frame of a projection without subscribing.\r\n *\r\n * The one-shot sibling of {@link useProjectionValue}, for consumers that are\r\n * not React components — the tab badge is a plain function called on every\r\n * tab-bar render, so it must neither hold a subscription nor allocate one.\r\n *\r\n * @param ctx - the client root context.\r\n * @param sessionId - the session whose projection is read.\r\n * @param key - the projection key to read.\r\n * @returns the raw snapshot: `undefined` while the capability is absent; the\r\n * semantics of any other value belong to the projection's own contract.\r\n */\r\nexport function projectionSnapshot(ctx: Context | undefined, sessionId: string | undefined, key: string): unknown {\r\n return resolveFace(ctx, sessionId, key).getSnapshot()\r\n}\r\n\r\n/**\r\n * Subscribe to one session projection by key.\r\n *\r\n * @param ctx - the client root context.\r\n * @param sessionId - the session whose projection is shown.\r\n * @param key - the projection key to subscribe to.\r\n * @returns the raw snapshot value, identity-stable across frames the host did\r\n * not change. Narrowing is the caller's job.\r\n */\r\nexport function useProjectionValue(ctx: Context | undefined, sessionId: string | undefined, key: string): unknown {\r\n const face = useMemo(() => resolveFace(ctx, sessionId, key), [ctx, sessionId, key])\r\n\r\n const subscribe = useCallback((listener: () => void) => face.subscribe(listener), [face])\r\n const getSnapshot = useCallback(() => face.getSnapshot(), [face])\r\n\r\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot)\r\n}\r\n","/**\r\n * Reading the live `todos` projection — the thin, todo-specific wrapper around\r\n * the generic face resolver (`../use-projection`). The projection key is\r\n * registered by `dsh-tool-todo`; the host pushes finished whole values and the\r\n * client never folds it. Narrowing lives in `./board` (`readTodos`).\r\n */\r\nimport { useMemo } from 'react'\r\nimport type { Context } from '@deepseek-ai/cordis'\r\nimport { readTodos } from './board'\r\nimport type { TodoItem } from './board'\r\nimport { useProjectionValue } from '../use-projection'\r\n\r\n/** The projection key registered by `dsh-tool-todo`. */\r\nexport const TODOS_KEY = 'todos'\r\n\r\n/**\r\n * Subscribe to one session's todo list.\r\n *\r\n * @param ctx - the client root context.\r\n * @param sessionId - the session whose projection is shown.\r\n * @returns the list, `[]` for a present-but-empty projection, or `undefined`\r\n * while the capability is absent.\r\n */\r\nexport function useTodos(ctx: Context | undefined, sessionId: string | undefined): readonly TodoItem[] | undefined {\r\n const raw = useProjectionValue(ctx, sessionId, TODOS_KEY)\r\n return useMemo(() => readTodos(raw), [raw])\r\n}\r\n","/**\r\n * The progress section: the `todos` projection drawn as one part of the\r\n * summary tab.\r\n *\r\n * The official composer-band panel is shadowed by this plugin (see\r\n * `brief/dock-shadow.tsx`), so the projection is read here and rendered as an\r\n * ordinary React view — no Canvas, no cross-package dependency. The full-height\r\n * shell and the scroll container live on the summary tab (`../SummaryTab`);\r\n * this component renders only its own slice and is the one section whose\r\n * absent state must stay VISIBLE: the official dock is hidden while this\r\n * plugin is mounted, so \"unavailable\" is a statement the reader needs.\r\n *\r\n * Colours come from `--dsw-alias-*` tokens only (skin contract), and the status\r\n * palette uses tokens that actually exist in the shipping theme:\r\n * `state-success-primary` / `state-business-primary` / `label-tertiary`.\r\n */\r\nimport { createElement } from 'react'\r\nimport type { CSSProperties, ReactNode } from 'react'\r\nimport type { Context } from '@deepseek-ai/cordis'\r\nimport { countTodos, progressLine, statusKey } from './brief/board'\r\nimport type { TodoItem, TodoStatus } from './brief/board'\r\nimport { useTodos } from './brief/use-todos'\r\n\r\n/** The session scope better-sidebar hands a tab body. */\r\nexport interface TodoScope {\r\n readonly sessionId?: string\r\n}\r\n\r\nexport interface TodoSectionProps {\r\n /** The DSH locale lookup, passed down from `apply`. */\r\n t: (key: string) => string\r\n /** The client root context (the tab body's only way to reach services). */\r\n ctx?: Context\r\n /** The session this tab is scoped to. */\r\n scope?: TodoScope\r\n}\r\n\r\n/** Status → token colour for the pill's text and border. */\r\nconst STATUS_COLOR: Record<TodoStatus, string> = {\r\n completed: 'var(--dsw-alias-state-success-primary)',\r\n in_progress: 'var(--dsw-alias-state-business-primary)',\r\n pending: 'var(--dsw-alias-label-tertiary)',\r\n}\r\n\r\nconst SECTION_STYLE: CSSProperties = {\r\n flexShrink: 0,\r\n borderBottom: '1px solid var(--dsw-alias-border-secondary)',\r\n}\r\n\r\nconst HEADER_STYLE: CSSProperties = {\r\n padding: '10px 12px 0',\r\n color: 'var(--dsw-alias-label-secondary)',\r\n fontSize: 12,\r\n fontWeight: 600,\r\n}\r\n\r\nconst PROGRESS_STYLE: CSSProperties = {\r\n padding: '6px 12px 10px',\r\n color: 'var(--dsw-alias-label-secondary)',\r\n fontSize: 12,\r\n}\r\n\r\nconst LIST_STYLE: CSSProperties = {\r\n listStyle: 'none',\r\n margin: 0,\r\n padding: '6px 0',\r\n display: 'flex',\r\n flexDirection: 'column',\r\n}\r\n\r\nconst ROW_STYLE: CSSProperties = {\r\n display: 'flex',\r\n alignItems: 'flex-start',\r\n gap: 8,\r\n padding: '6px 12px',\r\n}\r\n\r\n/** Pill style, coloured per status. */\r\nfunction pillStyle(status: TodoStatus): CSSProperties {\r\n return {\r\n flexShrink: 0,\r\n color: STATUS_COLOR[status],\r\n border: `1px solid ${STATUS_COLOR[status]}`,\r\n borderRadius: 999,\r\n padding: '0 6px',\r\n fontSize: 11,\r\n lineHeight: '16px',\r\n whiteSpace: 'nowrap',\r\n }\r\n}\r\n\r\n/** Task text: finished entries read as done, the working one as active. */\r\nfunction contentStyle(status: TodoStatus): CSSProperties {\r\n return {\r\n minWidth: 0,\r\n overflowWrap: 'anywhere',\r\n color:\r\n status === 'completed'\r\n ? 'var(--dsw-alias-label-tertiary)'\r\n : status === 'in_progress'\r\n ? 'var(--dsw-alias-label-primary)'\r\n : 'var(--dsw-alias-label-secondary)',\r\n textDecoration: status === 'completed' ? 'line-through' : 'none',\r\n }\r\n}\r\n\r\n/** Centred message block used by every non-list state. */\r\nexport function Notice(props: { title: string; detail?: string }): ReactNode {\r\n return (\r\n <div\r\n style={{\r\n margin: '0 auto',\r\n padding: 24,\r\n maxWidth: 460,\r\n textAlign: 'center',\r\n color: 'var(--dsw-alias-label-tertiary)',\r\n lineHeight: 1.7,\r\n fontSize: 12,\r\n }}\r\n >\r\n <div style={{ marginBottom: 6, color: 'var(--dsw-alias-label-secondary)' }}>{props.title}</div>\r\n {props.detail === undefined ? null : <div>{props.detail}</div>}\r\n </div>\r\n )\r\n}\r\n\r\n/** One task row. */\r\nfunction TodoRow(props: { item: TodoItem; t: (key: string) => string }): ReactNode {\r\n const { item, t } = props\r\n return createElement(\r\n 'li',\r\n { style: ROW_STYLE },\r\n createElement('span', { style: pillStyle(item.status) }, t(statusKey(item.status))),\r\n createElement('span', { style: contentStyle(item.status) }, item.content),\r\n )\r\n}\r\n\r\n/**\r\n * The progress section. The subscription is unconditional (a plain listener,\r\n * not IO); the three-state contract of the projection is preserved verbatim.\r\n * @param props - translation, client context, session scope.\r\n */\r\nexport function TodoSection(props: TodoSectionProps): ReactNode {\r\n const { t, ctx, scope } = props\r\n\r\n const todos = useTodos(ctx, scope?.sessionId)\r\n\r\n if (todos === undefined) {\r\n // `undefined` is the store's \"capability absent\": no session, or the host's\r\n // todo unit is unmounted. Say so rather than showing an empty board.\r\n return createElement(\r\n 'div',\r\n { style: SECTION_STYLE, 'data-dsh-brief-sidebar': 'progress-section' },\r\n createElement('div', { style: HEADER_STYLE }, t('section.progress')),\r\n createElement(Notice, { title: t('board.unavailable'), detail: t('board.unavailableHint') }),\r\n )\r\n }\r\n\r\n if (todos.length === 0) {\r\n return createElement(\r\n 'div',\r\n { style: SECTION_STYLE, 'data-dsh-brief-sidebar': 'progress-section' },\r\n createElement('div', { style: HEADER_STYLE }, t('section.progress')),\r\n createElement(Notice, { title: t('board.empty'), detail: t('board.emptyHint') }),\r\n )\r\n }\r\n\r\n const counts = countTodos(todos)\r\n\r\n return createElement(\r\n 'div',\r\n { style: SECTION_STYLE, 'data-dsh-brief-sidebar': 'progress-section' },\r\n createElement('div', { style: HEADER_STYLE }, t('section.progress')),\r\n createElement('div', { style: PROGRESS_STYLE }, progressLine(counts, t)),\r\n createElement(\r\n 'ul',\r\n { style: LIST_STYLE },\r\n todos.map((item, index) => createElement(TodoRow, { key: `${index}:${item.content}`, item, t })),\r\n ),\r\n )\r\n}\r\n\r\n/**\r\n * Backwards-compatible alias for the pre-summary export name. The component\r\n * no longer owns the full-height shell — that moved to `SummaryTab` — so the\r\n * alias exists only to keep import sites honest during the transition.\r\n */\r\nexport const TodoBoardTab = TodoSection\r\n","/**\r\n * The projection key this plugin contributes to the session-projection\r\n * registry, and its wire view.\r\n *\r\n * The key is namespaced with the plugin name on purpose: the registry refuses\r\n * to share a key across differing `stateVersion`s, so a collision with a\r\n * future host-side deliverables unit would be a hard load error, not a silent\r\n * shadow. The view type is declared structurally (the plugin has no compile\r\n * dependency on `@deepseek-ai/dsh-session-projection`); the registry accepts\r\n * any string key at runtime.\r\n */\r\n\r\n/** The projection key registered by this plugin's host half. */\r\nexport const DELIVERABLES_KEY = 'dshSummaryDeliverables'\r\n\r\n/** Client view of the produced-file fold (JSON-safe). */\r\nexport interface DeliverablesView {\r\n /** Paths produced in the latest turn, first-seen order (capped). */\r\n readonly latest: {\r\n readonly turn: number\r\n readonly paths: readonly string[]\r\n } | null\r\n /** Distinct produced paths session-wide, most-recent-first (capped). */\r\n readonly sessionPaths: readonly string[]\r\n /** Distinct produced paths session-wide, uncapped — the truthful total. */\r\n readonly sessionTotal: number\r\n}\r\n","/**\r\n * Opening a produced file's preview in the right Sidebar — the same channel\r\n * the conversation flow uses for its produced-file chips and inline mentions\r\n * (ui-chat `openFile` → `ctx.sidebarRight.openResource` with a\r\n * `dsh-resource://file/…` address).\r\n *\r\n * The address grammar is ported verbatim from the harness's browser-safe\r\n * `@deepseek-ai/dsh-util-workspace-path` (`file-address.ts` / `index.ts`),\r\n * because the plugin does not compile against that package: a relative path,\r\n * or an absolute path inside the session's workspace, is addressed under the\r\n * session's scope (`dsh-resource://file/session/<id>/<path>`); an absolute\r\n * path elsewhere keeps its absolute spelling in the same session's address.\r\n * Which tab type claims the address is the Sidebar's decision, not ours.\r\n *\r\n * The `sidebarRight` service is consumed structurally and treated as OPTIONAL:\r\n * where it is absent the section renders plain rows instead of links, the same\r\n * graceful degradation as the deliverables projection itself.\r\n */\r\nimport type { Context } from '@deepseek-ai/cordis'\r\n\r\n/** Structural view of the right-Sidebar service this plugin needs. */\r\nexport interface SidebarRightLike {\r\n openResource(url: string, options?: { params?: { line?: number } }): void\r\n}\r\n\r\n/** Structural view of the sessions service slice the opener reads. */\r\ninterface SessionsListLike {\r\n list?: { getSnapshot(): { byId?: Record<string, { cwd?: string }> } }\r\n}\r\n\r\n/** The address scheme every file resource opens with. */\r\nconst FILE_ADDRESS_PREFIX = 'dsh-resource://file/'\r\n\r\n/** Whether a path uses a Windows drive or UNC prefix. */\r\nfunction isWindowsStylePath(value: string): boolean {\r\n return /^[A-Za-z]:[/\\\\]/.test(value) || value.startsWith('\\\\\\\\')\r\n}\r\n\r\n/**\r\n * Whether a path is absolute in either spelling the Host accepts: POSIX (`/a/b`)\r\n * or Windows drive or UNC.\r\n */\r\nexport function isAbsoluteWorkspacePath(path: string): boolean {\r\n return path.startsWith('/') || isWindowsStylePath(path)\r\n}\r\n\r\n/** Component-encode one id or path segment, keeping `:` literal for drive letters. */\r\nfunction encodeSegment(segment: string): string {\r\n return encodeURIComponent(segment).replace(/%3A/gi, ':')\r\n}\r\n\r\n/** Encode a `/`-separated path segment by segment. */\r\nfunction encodePath(path: string): string {\r\n return path.split('/').map(encodeSegment).join('/')\r\n}\r\n\r\n/**\r\n * Build the address of a file read through one Session.\r\n * @param sessionId - the Session whose Host workspace resolves the path.\r\n * @param path - absolute or workspace-relative path; backslashes are normalized\r\n * to `/`, and leading `./` prefixes are dropped.\r\n */\r\nfunction sessionFileAddress(sessionId: string, path: string): string {\r\n const normalized = path.replace(/\\\\/g, '/').replace(/^(?:\\.\\/)+/, '')\r\n return `${FILE_ADDRESS_PREFIX}session/${encodeSegment(sessionId)}/${encodePath(normalized)}`\r\n}\r\n\r\n/**\r\n * The address for a path as a caller holds it.\r\n * @param sessionId - the Session the path is read in.\r\n * @param cwd - that Session's workspace root, when known.\r\n * @param path - absolute or workspace-relative path, in either separator spelling.\r\n */\r\nexport function fileAddressFor(sessionId: string, cwd: string | undefined, path: string): string {\r\n const normalized = path.replace(/\\\\/g, '/')\r\n if (!isAbsoluteWorkspacePath(normalized)) return sessionFileAddress(sessionId, normalized)\r\n const root = cwd === undefined ? '' : cwd.replace(/\\\\/g, '/').replace(/\\/+$/, '')\r\n if (root !== '' && normalized === root) return sessionFileAddress(sessionId, '')\r\n if (root !== '' && normalized.startsWith(`${root}/`)) {\r\n return sessionFileAddress(sessionId, normalized.slice(root.length + 1))\r\n }\r\n return sessionFileAddress(sessionId, normalized)\r\n}\r\n\r\n/**\r\n * Build a sidebar file opener for one session, or `undefined` when the\r\n * environment cannot open previews (no `sidebarRight` service, no session).\r\n * The cwd is re-read per call inside the opener, so a workspace switch between\r\n * renders is picked up on the next click.\r\n *\r\n * @param ctx - the client root context.\r\n * @param sessionId - the session whose workspace resolves paths.\r\n * @returns an opener that routes a produced path to the Sidebar preview.\r\n */\r\nexport function sidebarFileOpener(\r\n ctx: Context | undefined,\r\n sessionId: string | undefined,\r\n): ((path: string) => void) | undefined {\r\n if (ctx === undefined || sessionId === undefined) return undefined\r\n const bar = ctx.get('sidebarRight') as SidebarRightLike | undefined\r\n if (bar === undefined || typeof bar.openResource !== 'function') return undefined\r\n\r\n return (path: string) => {\r\n const sessions = ctx.get('sessions') as SessionsListLike | undefined\r\n const cwd = sessions?.list?.getSnapshot?.().byId?.[sessionId]?.cwd\r\n bar.openResource(fileAddressFor(sessionId, cwd, path))\r\n }\r\n}\r\n","/**\r\n * The `dshSummaryDeliverables` projection — reading and classifying it.\r\n *\r\n * The Host computes this projection (this plugin's own node half registers the\r\n * unit) and pushes finished whole values; the client never folds it. This\r\n * module is the read side of that contract and is deliberately PURE — no\r\n * React, no context, no IO — so the narrowing and the codeplan classification\r\n * can be tested directly.\r\n */\r\nimport type { DeliverablesView } from '../../projection/keys'\r\n\r\n/**\r\n * Narrow a raw projection value into a deliverables view.\r\n *\r\n * Two outcomes are kept apart on purpose, mirroring the store's contract:\r\n * - `undefined` — the capability is ABSENT (the host half never registered —\r\n * e.g. an older composition — or no frame carried the key yet);\r\n * - a view — the projection is live. A view with `latest: null` and\r\n * `sessionTotal: 0` IS the empty state; the fold starts publishing as soon\r\n * as the unit registers, so \"present but empty\" is a real value here, not a\r\n * sentinel.\r\n *\r\n * @param value - the raw `unknown` snapshot of the deliverables face.\r\n * @returns the validated view, or `undefined` when the capability is absent.\r\n */\r\nexport function readDeliverables(value: unknown): DeliverablesView | undefined {\r\n if (value === null || typeof value !== 'object' || Array.isArray(value)) return undefined\r\n const raw = value as { latest?: unknown; sessionPaths?: unknown; sessionTotal?: unknown }\r\n if (raw.latest === undefined || raw.sessionPaths === undefined || raw.sessionTotal === undefined) {\r\n return undefined\r\n }\r\n if (!isBucket(raw.latest) || !Array.isArray(raw.sessionPaths) || typeof raw.sessionTotal !== 'number') {\r\n return undefined\r\n }\r\n const paths = raw.sessionPaths.filter((path): path is string => typeof path === 'string')\r\n return {\r\n latest: raw.latest === null ? null : { turn: raw.latest.turn, paths: raw.latest.paths },\r\n sessionPaths: paths,\r\n sessionTotal: raw.sessionTotal,\r\n }\r\n}\r\n\r\n/** Whether a value is a well-formed latest-turn bucket (or explicitly null). */\r\nfunction isBucket(value: unknown): value is { turn: number; paths: readonly string[] } | null {\r\n if (value === null) return true\r\n if (typeof value !== 'object') return false\r\n const bucket = value as { turn?: unknown; paths?: unknown }\r\n return typeof bucket.turn === 'number' && Array.isArray(bucket.paths)\r\n}\r\n\r\n/**\r\n * The path prefix that identifies a codeplan artifact. The codeplan skill\r\n * writes its four fixed artifacts under `$workspace\\.agents\\plans\\<任务名>\\`;\r\n * produced paths may spell the separators either way.\r\n */\r\nconst CODEPLAN_SEGMENT = '.agents/plans/'\r\n\r\n/**\r\n * Normalize path separators so one spelling of the codeplan prefix matches.\r\n * Only classification uses this; the displayed path keeps the tool's exact\r\n * spelling.\r\n */\r\nfunction normalized(path: string): string {\r\n return path.replaceAll('\\\\', '/')\r\n}\r\n\r\n/** Whether a produced path is a codeplan artifact. */\r\nexport function isCodeplanPath(path: string): boolean {\r\n return normalized(path).includes(CODEPLAN_SEGMENT)\r\n}\r\n\r\n/**\r\n * Split a codeplan path into its task name and file name.\r\n *\r\n * @param path - a produced path; need not actually be a codeplan artifact.\r\n * @returns task and file names, or `null` when the path is not under the\r\n * codeplan plans directory (or names no file beneath the task folder).\r\n */\r\nexport function splitCodeplanPath(path: string): { task: string; file: string } | null {\r\n const marker = normalized(path).indexOf(CODEPLAN_SEGMENT)\r\n if (marker < 0) return null\r\n const rest = normalized(path).slice(marker + CODEPLAN_SEGMENT.length)\r\n const slash = rest.indexOf('/')\r\n if (slash <= 0 || slash === rest.length - 1) return null\r\n return { task: rest.slice(0, slash), file: rest.slice(slash + 1) }\r\n}\r\n\r\n/** The final path segment, under either separator. */\r\nexport function basename(path: string): string {\r\n const normalizedPath = normalized(path)\r\n const slash = normalizedPath.lastIndexOf('/')\r\n return slash < 0 ? normalizedPath : normalizedPath.slice(slash + 1)\r\n}\r\n\r\n/**\r\n * 历史文件 = 全会话去重路径 − 本回合活跃路径。\r\n *\r\n * 分隔符归一化后比较(`a\\b` 与 `a/b` 视为同一路径),保持 sessionPaths 自带的\r\n * \"最近在前\"顺序;活跃路径一个都不出现——分层契约:活跃的已在上方列表里,\r\n * 折叠区只收\"更早改过的文件\"。\r\n */\r\nexport function historyPaths(sessionPaths: readonly string[], latestPaths: readonly string[]): string[] {\r\n const active = new Set(latestPaths.map(normalized))\r\n return sessionPaths.filter((path) => !active.has(normalized(path)))\r\n}\r\n","/**\r\n * Plugin-owned i18n dictionaries.\r\n *\r\n * Consumer plugins must NOT reach into better-sidebar's internal `t()` or its\r\n * `betterSidebar` dictionary namespace, so this plugin registers its own\r\n * namespace through the DSH `locale` service. `dsh-brief-sidebar` is outside the\r\n * `LocaleNamespaceMap` merge table, which is exactly what the untyped\r\n * `register(ns, dicts)` overload is for — but the dictionaries themselves stay\r\n * strictly paired: `zh` is the key-set source of truth and every other language\r\n * (`en`, plus the fr/de/it/ru/es pack) is typed as\r\n * `Record<keyof typeof zh, string>`, so a missing or extra key is a COMPILE\r\n * error rather than a runtime fallback to the raw key.\r\n *\r\n * Every key referenced anywhere in `src/**` must exist in both dictionaries;\r\n * `tests/purity.spec.ts` asserts that, so a typo in a `t('…')` call cannot\r\n * ship as an untranslated literal.\r\n */\r\n\r\n/** The namespace owned by this plugin (own vocabulary, own lifecycle). */\r\nexport const NS = 'dsh-brief-sidebar'\r\n\r\n/** Simplified Chinese dictionary — the key-set source of truth. */\r\nexport const zh = {\r\n 'tab.title': '概要',\r\n 'tab.desc': '当前会话的任务、产物与规划文件(只读)',\r\n 'section.progress': '进展',\r\n 'section.deliverables': '产物',\r\n 'board.empty': '当前没有任务',\r\n 'board.emptyHint': '模型调用 todo_write 之后,任务会出现在这里。',\r\n 'board.unavailable': '任务暂不可用',\r\n 'board.unavailableHint': '这个会话还没有收到 todo 数据;宿主推来第一帧后会自动显示。',\r\n 'status.pending': '待处理',\r\n 'status.inProgress': '进行中',\r\n 'status.completed': '已完成',\r\n 'deliverables.empty': '本回合暂无改动文件',\r\n 'deliverables.emptyHint': '会话里成功写入或修改文件后,它们会实时出现在这里。',\r\n 'deliverables.history': '更早改动的文件 · {count}',\r\n 'deliverable.codeplanTag': '规划',\r\n 'section.memorySlots': '记忆',\r\n 'slots.empty': '当前没有激活的记忆槽位',\r\n 'slots.emptyHint': '当 dsh-prime-memory 激活记忆槽位后,它们会实时出现在这里。',\r\n 'slots.openCount': '开启 {count} 个槽位',\r\n 'slots.status.open': '生效中',\r\n 'slots.status.done': '已完成',\r\n 'slots.status.dropped': '已弃置',\r\n 'slots.status.expired': '已过期',\r\n 'settings.title': '概要侧栏',\r\n 'settings.desc': '右侧栏概要 tab 的分区开关(只读展示)。',\r\n 'settings.memorySlots': '记忆槽位分区',\r\n 'settings.memorySlotsHint': '开启后,概要 tab 在进展与产物之下显示 dsh-prime-memory 的激活记忆槽位(只读)。',\r\n 'settings.unavailable': '配置面不可用,记忆分区按默认「开」处理。',\r\n 'settings.readonly': '当前配置只读,无法在此修改。',\r\n}\r\n\r\n/** The key union: what a `t('…')` call may name inside this namespace. */\r\nexport type BriefKey = keyof typeof zh\r\n\r\n/** English dictionary, checked complete against the zh key set. */\r\nexport const en: Record<BriefKey, string> = {\r\n 'tab.title': 'Summary',\r\n 'tab.desc': 'Tasks, produced files and plan artifacts of this session (read-only)',\r\n 'section.progress': 'Progress',\r\n 'section.deliverables': 'Produced files',\r\n 'board.empty': 'No tasks yet',\r\n 'board.emptyHint': 'Tasks show up here once the model calls todo_write.',\r\n 'board.unavailable': 'Tasks unavailable',\r\n 'board.unavailableHint':\r\n 'This session has not received any todo data yet; the board fills in on the first frame.',\r\n 'status.pending': 'Pending',\r\n 'status.inProgress': 'In progress',\r\n 'status.completed': 'Done',\r\n 'deliverables.empty': 'No files changed in this turn',\r\n 'deliverables.emptyHint': 'Files written or edited successfully in the session appear here live.',\r\n 'deliverables.history': 'Earlier changes · {count}',\r\n 'deliverable.codeplanTag': 'Plan',\r\n 'section.memorySlots': 'Memory',\r\n 'slots.empty': 'No active memory slots',\r\n 'slots.emptyHint': 'Slots appear here live once dsh-prime-memory activates them.',\r\n 'slots.openCount': '{count} slots open',\r\n 'slots.status.open': 'Open',\r\n 'slots.status.done': 'Done',\r\n 'slots.status.dropped': 'Dropped',\r\n 'slots.status.expired': 'Expired',\r\n 'settings.title': 'Brief sidebar',\r\n 'settings.desc': 'Section switches for the summary tab in the right sidebar (read-only views).',\r\n 'settings.memorySlots': 'Memory slots section',\r\n 'settings.memorySlotsHint': 'When on, the summary tab lists the active memory slots from dsh-prime-memory beneath progress and produced files (read-only).',\r\n 'settings.unavailable': 'The config face is unavailable; the memory section falls back to its default (on).',\r\n 'settings.readonly': 'This config is read-only here; edits are not available.',\r\n}\r\n\r\n/** Japanese dictionary, checked complete against the zh key set. */\r\nexport const ja: Record<BriefKey, string> = {\r\n 'tab.title': '概要',\r\n 'tab.desc': 'このセッションのタスク・成果物・計画ファイル(読み取り専用)',\r\n 'section.progress': '進行状況',\r\n 'section.deliverables': '成果物',\r\n 'board.empty': '現在タスクはありません',\r\n 'board.emptyHint': 'モデルが todo_write を呼び出すと、タスクがここに表示されます。',\r\n 'board.unavailable': 'タスクを利用できません',\r\n 'board.unavailableHint':\r\n 'このセッションはまだ todo データを受信していません。ホストが最初のフレームを送ると自動的に表示されます。',\r\n 'status.pending': '未対応',\r\n 'status.inProgress': '進行中',\r\n 'status.completed': '完了',\r\n 'deliverables.empty': 'このターンで変更されたファイルはありません',\r\n 'deliverables.emptyHint': 'セッションでファイルの書き込み・変更が成功すると、ここにリアルタイムで表示されます。',\r\n 'deliverables.history': '以前の変更 · {count}',\r\n 'deliverable.codeplanTag': '計画',\r\n 'section.memorySlots': 'メモリ',\r\n 'slots.empty': 'アクティブなメモリスロットはありません',\r\n 'slots.emptyHint': 'dsh-prime-memory がスロットをアクティブにすると、ここにリアルタイムで表示されます。',\r\n 'slots.openCount': '有効なスロット: {count} 件',\r\n 'slots.status.open': '有効',\r\n 'slots.status.done': '完了',\r\n 'slots.status.dropped': '破棄',\r\n 'slots.status.expired': '期限切れ',\r\n 'settings.title': '概要サイドバー',\r\n 'settings.desc': '右サイドバー概要タブのセクション表示切り替え(読み取り専用)。',\r\n 'settings.memorySlots': 'メモリスロットセクション',\r\n 'settings.memorySlotsHint': 'オンにすると、概要タブの進行状況と成果物の下に dsh-prime-memory のアクティブなメモリスロット(読み取り専用)が表示されます。',\r\n 'settings.unavailable': '設定画面を利用できません。メモリセクションは既定(オン)で動作します。',\r\n 'settings.readonly': 'この設定はここでは読み取り専用のため変更できません。',\r\n}\r\n\r\n/** Korean dictionary, checked complete against the zh key set. */\r\nexport const ko: Record<BriefKey, string> = {\r\n 'tab.title': '요약',\r\n 'tab.desc': '현재 세션의 작업, 산출물 및 계획 파일(읽기 전용)',\r\n 'section.progress': '진행 상황',\r\n 'section.deliverables': '산출물',\r\n 'board.empty': '현재 작업이 없습니다',\r\n 'board.emptyHint': '모델이 todo_write를 호출하면 작업이 여기에 표시됩니다.',\r\n 'board.unavailable': '작업을 사용할 수 없습니다',\r\n 'board.unavailableHint':\r\n '이 세션은 아직 todo 데이터를 받지 못했습니다. 호스트가 첫 프레임을 보내면 자동으로 표시됩니다.',\r\n 'status.pending': '대기 중',\r\n 'status.inProgress': '진행 중',\r\n 'status.completed': '완료',\r\n 'deliverables.empty': '이번 턴에 변경된 파일이 없습니다',\r\n 'deliverables.emptyHint': '세션에서 파일을 성공적으로 작성하거나 수정하면 여기에 실시간으로 표시됩니다.',\r\n 'deliverables.history': '이전 변경 사항 · {count}',\r\n 'deliverable.codeplanTag': '계획',\r\n 'section.memorySlots': '메모리',\r\n 'slots.empty': '활성화된 메모리 슬롯이 없습니다',\r\n 'slots.emptyHint': 'dsh-prime-memory가 슬롯을 활성화하면 여기에 실시간으로 표시됩니다.',\r\n 'slots.openCount': '열린 슬롯: {count}개',\r\n 'slots.status.open': '활성',\r\n 'slots.status.done': '완료',\r\n 'slots.status.dropped': '폐기',\r\n 'slots.status.expired': '만료',\r\n 'settings.title': '요약 사이드바',\r\n 'settings.desc': '오른쪽 사이드바 요약 탭의 섹션 표시 전환(읽기 전용).',\r\n 'settings.memorySlots': '메모리 슬롯 섹션',\r\n 'settings.memorySlotsHint': '켜면 요약 탭의 진행 상황과 산출물 아래에 dsh-prime-memory의 활성 메모리 슬롯(읽기 전용)이 표시됩니다.',\r\n 'settings.unavailable': '설정 화면을 사용할 수 없어 메모리 섹션은 기본값(켜짐)으로 동작합니다.',\r\n 'settings.readonly': '이 설정은 여기서 읽기 전용이므로 변경할 수 없습니다.',\r\n}\r\n\r\n/** French dictionary, checked complete against the zh key set. */\r\nexport const fr: Record<BriefKey, string> = {\r\n 'tab.title': 'Résumé',\r\n 'tab.desc': 'Tâches, fichiers produits et plans de la session (lecture seule)',\r\n 'section.progress': 'Avancement',\r\n 'section.deliverables': 'Fichiers produits',\r\n 'board.empty': 'Aucune tâche pour le moment',\r\n 'board.emptyHint': 'Les tâches apparaissent ici dès que le modèle appelle todo_write.',\r\n 'board.unavailable': 'Tâches indisponibles',\r\n 'board.unavailableHint':\r\n 'Cette session n’a pas encore reçu de données todo ; le tableau se remplit dès la première trame.',\r\n 'status.pending': 'En attente',\r\n 'status.inProgress': 'En cours',\r\n 'status.completed': 'Terminé',\r\n 'deliverables.empty': 'Aucun fichier modifié à ce tour',\r\n 'deliverables.emptyHint': 'Les fichiers écrits ou modifiés dans la session apparaissent ici en direct.',\r\n 'deliverables.history': 'Modifications antérieures · {count}',\r\n 'deliverable.codeplanTag': 'Plan',\r\n 'section.memorySlots': 'Mémoire',\r\n 'slots.empty': 'Aucun emplacement mémoire actif',\r\n 'slots.emptyHint': 'Les emplacements apparaissent ici en direct dès que dsh-prime-memory les active.',\r\n 'slots.openCount': '{count} emplacements ouverts',\r\n 'slots.status.open': 'Actif',\r\n 'slots.status.done': 'Terminé',\r\n 'slots.status.dropped': 'Abandonné',\r\n 'slots.status.expired': 'Expiré',\r\n 'settings.title': 'Barre latérale Résumé',\r\n 'settings.desc': 'Interrupteurs des sections de l’onglet Résumé de la barre latérale (lecture seule).',\r\n 'settings.memorySlots': 'Section des emplacements mémoire',\r\n 'settings.memorySlotsHint': 'Une fois activé, l’onglet Résumé liste les emplacements mémoire actifs de dsh-prime-memory sous l’avancement et les fichiers produits (lecture seule).',\r\n 'settings.unavailable': 'Le canal de configuration est indisponible ; la section mémoire revient à sa valeur par défaut (activée).',\r\n 'settings.readonly': 'Cette configuration est en lecture seule ici ; aucune modification possible.',\r\n}\r\n\r\n/** German dictionary, checked complete against the zh key set. */\r\nexport const de: Record<BriefKey, string> = {\r\n 'tab.title': 'Übersicht',\r\n 'tab.desc': 'Aufgaben, erzeugte Dateien und Pläne dieser Sitzung (schreibgeschützt)',\r\n 'section.progress': 'Fortschritt',\r\n 'section.deliverables': 'Erzeugte Dateien',\r\n 'board.empty': 'Noch keine Aufgaben',\r\n 'board.emptyHint': 'Aufgaben erscheinen hier, sobald das Modell todo_write aufruft.',\r\n 'board.unavailable': 'Aufgaben nicht verfügbar',\r\n 'board.unavailableHint':\r\n 'Diese Sitzung hat noch keine Todo-Daten erhalten; das Board füllt sich mit dem ersten Frame.',\r\n 'status.pending': 'Ausstehend',\r\n 'status.inProgress': 'In Bearbeitung',\r\n 'status.completed': 'Fertig',\r\n 'deliverables.empty': 'Keine Dateien in diesem Durchgang geändert',\r\n 'deliverables.emptyHint': 'In der Sitzung erfolgreich geschriebene oder geänderte Dateien erscheinen hier live.',\r\n 'deliverables.history': 'Frühere Änderungen · {count}',\r\n 'deliverable.codeplanTag': 'Plan',\r\n 'section.memorySlots': 'Gedächtnis',\r\n 'slots.empty': 'Keine aktiven Speicherplätze',\r\n 'slots.emptyHint': 'Sobald dsh-prime-memory Slots aktiviert, erscheinen sie hier in Echtzeit.',\r\n 'slots.openCount': '{count} Slots offen',\r\n 'slots.status.open': 'Offen',\r\n 'slots.status.done': 'Erledigt',\r\n 'slots.status.dropped': 'Verworfen',\r\n 'slots.status.expired': 'Abgelaufen',\r\n 'settings.title': 'Übersicht-Seitenleiste',\r\n 'settings.desc': 'Schalter für die Abschnitte des Übersicht-Tabs in der rechten Seitenleiste (schreibgeschützt).',\r\n 'settings.memorySlots': 'Speicherplätze-Abschnitt',\r\n 'settings.memorySlotsHint': 'Ist der Schalter eingeschaltet, listet der Übersicht-Tab die aktiven Speicherplätze aus dsh-prime-memory unter Fortschritt und erzeugten Dateien auf (schreibgeschützt).',\r\n 'settings.unavailable': 'Die Konfigurationsoberfläche ist nicht verfügbar; der Gedächtnis-Abschnitt fällt auf den Standard (ein) zurück.',\r\n 'settings.readonly': 'Diese Konfiguration ist hier schreibgeschützt; Änderungen sind nicht möglich.',\r\n}\r\n\r\n/** Italian dictionary, checked complete against the zh key set. */\r\nexport const it: Record<BriefKey, string> = {\r\n 'tab.title': 'Riepilogo',\r\n 'tab.desc': 'Attività, file prodotti e piani di questa sessione (sola lettura)',\r\n 'section.progress': 'Avanzamento',\r\n 'section.deliverables': 'File prodotti',\r\n 'board.empty': 'Nessuna attività per ora',\r\n 'board.emptyHint': 'Le attività appaiono qui quando il modello chiama todo_write.',\r\n 'board.unavailable': 'Attività non disponibili',\r\n 'board.unavailableHint':\r\n 'Questa sessione non ha ancora ricevuto dati todo; la bacheca si riempirà al primo frame.',\r\n 'status.pending': 'In attesa',\r\n 'status.inProgress': 'In corso',\r\n 'status.completed': 'Completato',\r\n 'deliverables.empty': 'Nessun file modificato in questo turno',\r\n 'deliverables.emptyHint': 'I file scritti o modificati nella sessione appaiono qui in tempo reale.',\r\n 'deliverables.history': 'Modifiche precedenti · {count}',\r\n 'deliverable.codeplanTag': 'Piano',\r\n 'section.memorySlots': 'Memoria',\r\n 'slots.empty': 'Nessuno slot di memoria attivo',\r\n 'slots.emptyHint': 'Gli slot appaiono qui in tempo reale quando dsh-prime-memory li attiva.',\r\n 'slots.openCount': '{count} slot aperti',\r\n 'slots.status.open': 'Attivo',\r\n 'slots.status.done': 'Completato',\r\n 'slots.status.dropped': 'Scartato',\r\n 'slots.status.expired': 'Scaduto',\r\n 'settings.title': 'Barra laterale Riepilogo',\r\n 'settings.desc': 'Interruttori delle sezioni della scheda Riepilogo nella barra laterale destra (sola lettura).',\r\n 'settings.memorySlots': 'Sezione slot di memoria',\r\n 'settings.memorySlotsHint': 'Quando attivo, la scheda Riepilogo elenca gli slot di memoria attivi di dsh-prime-memory sotto avanzamento e file prodotti (sola lettura).',\r\n 'settings.unavailable': 'La superficie di configurazione non è disponibile; la sezione memoria torna al valore predefinito (attivo).',\r\n 'settings.readonly': 'Questa configurazione è in sola lettura qui; non è possibile modificarla.',\r\n}\r\n\r\n/** Russian dictionary, checked complete against the zh key set. */\r\nexport const ru: Record<BriefKey, string> = {\r\n 'tab.title': 'Сводка',\r\n 'tab.desc': 'Задачи, созданные файлы и планы этой сессии (только чтение)',\r\n 'section.progress': 'Ход работы',\r\n 'section.deliverables': 'Созданные файлы',\r\n 'board.empty': 'Задач пока нет',\r\n 'board.emptyHint': 'Задачи появятся здесь, когда модель вызовет todo_write.',\r\n 'board.unavailable': 'Задачи недоступны',\r\n 'board.unavailableHint':\r\n 'Эта сессия ещё не получила данные todo; доска заполнится после первого кадра.',\r\n 'status.pending': 'Ожидает',\r\n 'status.inProgress': 'В работе',\r\n 'status.completed': 'Готово',\r\n 'deliverables.empty': 'За этот ход файлы не менялись',\r\n 'deliverables.emptyHint': 'Успешно записанные или изменённые файлы появляются здесь в реальном времени.',\r\n 'deliverables.history': 'Более ранние изменения · {count}',\r\n 'deliverable.codeplanTag': 'План',\r\n 'section.memorySlots': 'Память',\r\n 'slots.empty': 'Активных слотов памяти нет',\r\n 'slots.emptyHint': 'Как только dsh-prime-memory активирует слоты, они появляются здесь в реальном времени.',\r\n 'slots.openCount': 'Открытых слотов: {count}',\r\n 'slots.status.open': 'Активен',\r\n 'slots.status.done': 'Завершён',\r\n 'slots.status.dropped': 'Отброшен',\r\n 'slots.status.expired': 'Истёк',\r\n 'settings.title': 'Боковая панель «Сводка»',\r\n 'settings.desc': 'Переключатели разделов вкладки «Сводка» на правой панели (только чтение).',\r\n 'settings.memorySlots': 'Раздел слотов памяти',\r\n 'settings.memorySlotsHint': 'Когда включено, вкладка «Сводка» показывает активные слоты памяти из dsh-prime-memory под ходом работы и созданными файлами (только чтение).',\r\n 'settings.unavailable': 'Панель конфигурации недоступна; раздел памяти работает по умолчанию (включён).',\r\n 'settings.readonly': 'Эта конфигурация здесь только для чтения; изменить её нельзя.',\r\n}\r\n\r\n/** Spanish dictionary, checked complete against the zh key set. */\r\nexport const es: Record<BriefKey, string> = {\r\n 'tab.title': 'Resumen',\r\n 'tab.desc': 'Tareas, archivos generados y planes de esta sesión (solo lectura)',\r\n 'section.progress': 'Avance',\r\n 'section.deliverables': 'Archivos generados',\r\n 'board.empty': 'Aún no hay tareas',\r\n 'board.emptyHint': 'Las tareas aparecen aquí cuando el modelo llama a todo_write.',\r\n 'board.unavailable': 'Tareas no disponibles',\r\n 'board.unavailableHint':\r\n 'Esta sesión aún no ha recibido datos de tareas; el tablero se llena con el primer fotograma.',\r\n 'status.pending': 'Pendiente',\r\n 'status.inProgress': 'En curso',\r\n 'status.completed': 'Hecho',\r\n 'deliverables.empty': 'Ningún archivo modificado en este turno',\r\n 'deliverables.emptyHint': 'Los archivos escritos o editados en la sesión aparecen aquí en directo.',\r\n 'deliverables.history': 'Cambios anteriores · {count}',\r\n 'deliverable.codeplanTag': 'Plan',\r\n 'section.memorySlots': 'Memoria',\r\n 'slots.empty': 'No hay ranuras de memoria activas',\r\n 'slots.emptyHint': 'Las ranuras aparecen aquí en directo en cuanto dsh-prime-memory las active.',\r\n 'slots.openCount': '{count} ranuras abiertas',\r\n 'slots.status.open': 'Activa',\r\n 'slots.status.done': 'Hecha',\r\n 'slots.status.dropped': 'Descartada',\r\n 'slots.status.expired': 'Caducada',\r\n 'settings.title': 'Barra lateral Resumen',\r\n 'settings.desc': 'Interruptores de las secciones de la pestaña Resumen en la barra lateral derecha (solo lectura).',\r\n 'settings.memorySlots': 'Sección de ranuras de memoria',\r\n 'settings.memorySlotsHint': 'Al activarla, la pestaña Resumen enumera las ranuras de memoria activas de dsh-prime-memory bajo el avance y los archivos generados (solo lectura).',\r\n 'settings.unavailable': 'La superficie de configuración no está disponible; la sección de memoria vuelve a su valor por defecto (activada).',\r\n 'settings.readonly': 'Esta configuración es de solo lectura aquí; no se puede modificar.',\r\n}\r\n\r\n/**\r\n * All locales in the shape the locale service consumes. Registered in ONE call:\r\n * the registry rejects a duplicate `(namespace, locale)` pair and the\r\n * per-locale form would leave a half-registered namespace if a later call\r\n * threw. `zh`/`en` are the host built-ins; the fr/de/it/ru/es pack rides the\r\n * same map form (every id is a valid BCP 47-style tag, so the registry accepts\r\n * it as-is).\r\n */\r\nexport const dictionaries: Record<string, Record<string, string>> = {\r\n zh,\r\n en,\r\n ja,\r\n ko,\r\n fr,\r\n de,\r\n it,\r\n ru,\r\n es,\r\n}\r\n\r\n/**\r\n * Expand the one `{count}` placeholder the deliverables totals line uses. The\r\n * locale binder is a plain key → string lookup (no interpolation machinery),\r\n * mirroring how `progressLine` composes its summary from parts.\r\n */\r\nexport function expandCount(template: string, count: number): string {\r\n return template.replace('{count}', String(count))\r\n}\r\n","/**\r\n * The deliverables section: the `dshSummaryDeliverables` projection drawn as\r\n * one part of the summary tab.\r\n *\r\n * Unlike the progress section this is an OPTIONAL capability: where the host\r\n * half never registered the unit (older composition, key absent), the section\r\n * hides entirely — absence is not an error state here, and the official\r\n * \"本次产出\" row in the conversation flow still covers the turn-end view.\r\n * When the projection is live, the latest turn's produced paths update\r\n * mid-turn: every successful mutation result publishes a frame, so the reader\r\n * sees files appear while the turn is still running, which is exactly the gap\r\n * the official turn-tail row has.\r\n *\r\n * Codeplan artifacts (paths under `.agents/plans/<任务名>/`) are ordinary\r\n * produced entries — they are marked with a pill, not split into a separate\r\n * section.\r\n *\r\n * Colours come from `--dsw-alias-*` tokens only (skin contract).\r\n */\r\nimport { createElement } from 'react'\r\nimport { useState } from 'react'\r\nimport type { CSSProperties, ReactNode } from 'react'\r\nimport type { Context } from '@deepseek-ai/cordis'\r\nimport { DELIVERABLES_KEY } from '../../projection/keys'\r\nimport { useProjectionValue } from '../use-projection'\r\nimport { sidebarFileOpener } from '../file-open'\r\nimport { basename, historyPaths, readDeliverables, splitCodeplanPath } from './deliverables'\r\nimport { expandCount } from '../locales'\r\n\r\nexport interface DeliverablesSectionProps {\r\n /** The DSH locale lookup, passed down from `apply`. */\r\n t: (key: string) => string\r\n /** The client root context (the tab body's only way to reach services). */\r\n ctx?: Context\r\n /** The session this tab is scoped to. */\r\n scope?: { readonly sessionId?: string }\r\n}\r\n\r\nconst SECTION_STYLE: CSSProperties = {\r\n flexShrink: 0,\r\n borderBottom: '1px solid var(--dsw-alias-border-secondary)',\r\n}\r\n\r\nconst HEADER_STYLE: CSSProperties = {\r\n padding: '10px 12px 0',\r\n color: 'var(--dsw-alias-label-secondary)',\r\n fontSize: 12,\r\n fontWeight: 600,\r\n}\r\n\r\nconst LIST_STYLE: CSSProperties = {\r\n listStyle: 'none',\r\n margin: 0,\r\n padding: '6px 0',\r\n display: 'flex',\r\n flexDirection: 'column',\r\n}\r\n\r\nconst ROW_STYLE: CSSProperties = {\r\n display: 'flex',\r\n alignItems: 'flex-start',\r\n gap: 8,\r\n padding: '6px 12px',\r\n}\r\n\r\n/** 折叠区开关行:与文件行同宽,tertiary 强调,整行可点。 */\r\nconst HISTORY_TOGGLE_STYLE: CSSProperties = {\r\n background: 'none',\r\n border: 'none',\r\n font: 'inherit',\r\n fontSize: 12,\r\n color: 'var(--dsw-alias-label-tertiary)',\r\n cursor: 'pointer',\r\n display: 'flex',\r\n alignItems: 'center',\r\n gap: 4,\r\n padding: '6px 12px',\r\n textAlign: 'left',\r\n width: 'calc(100% - 24px)',\r\n}\r\n\r\nconst CHEVRON_STYLE = (open: boolean): CSSProperties => ({\r\n flexShrink: 0,\r\n transition: 'transform 0.16s',\r\n transform: open ? 'rotate(180deg)' : 'none',\r\n})\r\n\r\nconst PATH_STYLE: CSSProperties = {\r\n minWidth: 0,\r\n overflowWrap: 'anywhere',\r\n color: 'var(--dsw-alias-label-secondary)',\r\n}\r\n\r\n/** Row-as-button reset: the row itself is the click target, content unchanged. */\r\nconst BUTTON_STYLE: CSSProperties = {\r\n ...PATH_STYLE,\r\n background: 'none',\r\n border: 'none',\r\n font: 'inherit',\r\n fontSize: 'inherit',\r\n color: 'var(--dsw-alias-label-secondary)',\r\n padding: 0,\r\n textAlign: 'left',\r\n cursor: 'pointer',\r\n}\r\n\r\n/** The codeplan pill: same form as the status pills, tertiary emphasis. */\r\nconst TAG_STYLE: CSSProperties = {\r\n flexShrink: 0,\r\n color: 'var(--dsw-alias-label-tertiary)',\r\n border: '1px solid var(--dsw-alias-label-tertiary)',\r\n borderRadius: 999,\r\n padding: '0 6px',\r\n fontSize: 11,\r\n lineHeight: '16px',\r\n whiteSpace: 'nowrap',\r\n}\r\n\r\n/**\r\n * One produced-file row. When the sidebar opener is available the row is a\r\n * button that routes the path to the Sidebar preview (the same channel the\r\n * conversation's produced-file chips use); otherwise it degrades to a plain\r\n * span. A codeplan artifact carries the pill either way.\r\n */\r\nfunction PathRow(props: {\r\n path: string\r\n t: (key: string) => string\r\n open?: (path: string) => void\r\n}): ReactNode {\r\n const { path, t, open } = props\r\n const plan = splitCodeplanPath(path)\r\n const title = plan === null ? path : `${t('deliverable.codeplanTag')}: ${plan.task} · ${path}`\r\n const label = basename(path)\r\n const content = plan === null ? null : createElement('span', { style: TAG_STYLE }, t('deliverable.codeplanTag'))\r\n if (open === undefined) {\r\n return createElement(\r\n 'li',\r\n { style: ROW_STYLE },\r\n content,\r\n createElement('span', { style: PATH_STYLE, title }, label),\r\n )\r\n }\r\n return createElement(\r\n 'li',\r\n { style: ROW_STYLE },\r\n content,\r\n createElement('button', { style: BUTTON_STYLE, title, onClick: () => { open(path) } }, label),\r\n )\r\n}\r\n\r\n/**\r\n * The deliverables section. The subscription is unconditional (a plain\r\n * listener, not IO).\r\n *\r\n * 分层:活跃文件(本回合 latest)直接列表;全会话更早改动折叠在开关行后面\r\n * (与活跃文件按归一化路径去重),展开后是与活跃区相同的可点击文件行。\r\n * @param props - translation, client context, session scope.\r\n */\r\nexport function DeliverablesSection(props: DeliverablesSectionProps): ReactNode {\r\n const { t, ctx, scope } = props\r\n const [historyOpen, setHistoryOpen] = useState(false)\r\n\r\n const view = readDeliverables(useProjectionValue(ctx, scope?.sessionId, DELIVERABLES_KEY))\r\n\r\n // Capability absent — the optional unit never registered. Hide, don't nag.\r\n if (view === undefined) return null\r\n\r\n // Optional click-to-preview: rows degrade to plain text where the sidebar\r\n // opener is absent (the opener resolves the session cwd per click).\r\n const open = sidebarFileOpener(ctx, scope?.sessionId)\r\n\r\n const latestPaths = view.latest?.paths ?? []\r\n const history = historyPaths(view.sessionPaths, latestPaths)\r\n\r\n if (latestPaths.length === 0 && view.sessionTotal === 0) {\r\n return createElement(\r\n 'div',\r\n { style: SECTION_STYLE, 'data-dsh-brief-sidebar': 'deliverables-section' },\r\n createElement('div', { style: HEADER_STYLE }, t('section.deliverables')),\r\n createElement(\r\n 'div',\r\n {\r\n style: {\r\n margin: '0 auto',\r\n padding: 24,\r\n maxWidth: 460,\r\n textAlign: 'center',\r\n color: 'var(--dsw-alias-label-tertiary)',\r\n lineHeight: 1.7,\r\n fontSize: 12,\r\n },\r\n },\r\n createElement('div', { style: { marginBottom: 6, color: 'var(--dsw-alias-label-secondary)' } }, t('deliverables.empty')),\r\n createElement('div', null, t('deliverables.emptyHint')),\r\n ),\r\n )\r\n }\r\n\r\n return createElement(\r\n 'div',\r\n { style: SECTION_STYLE, 'data-dsh-brief-sidebar': 'deliverables-section' },\r\n createElement('div', { style: HEADER_STYLE }, t('section.deliverables')),\r\n createElement(\r\n 'ul',\r\n { style: LIST_STYLE },\r\n latestPaths.map(path => createElement(PathRow, { key: path, path, t, open })),\r\n ),\r\n history.length > 0 && createElement(\r\n 'button',\r\n {\r\n type: 'button',\r\n 'aria-expanded': historyOpen,\r\n style: HISTORY_TOGGLE_STYLE,\r\n onClick: () => { setHistoryOpen(current => !current) },\r\n },\r\n expandCount(t('deliverables.history'), history.length),\r\n createElement(\r\n 'svg',\r\n { width: 10, height: 10, viewBox: '0 0 16 16', 'aria-hidden': true, style: CHEVRON_STYLE(historyOpen) },\r\n createElement('path', {\r\n d: 'M4 6l4 4 4-4',\r\n fill: 'none',\r\n stroke: 'currentColor',\r\n strokeWidth: 1.5,\r\n strokeLinecap: 'round',\r\n strokeLinejoin: 'round',\r\n }),\r\n ),\r\n ),\r\n historyOpen && createElement(\r\n 'ul',\r\n { style: LIST_STYLE },\r\n history.map(path => createElement(PathRow, { key: path, path, t, open })),\r\n ),\r\n )\r\n}\r\n","/**\r\n * The `memorySlots` projection — reading it.\r\n *\r\n * The projection unit is registered by `dsh-prime-memory` (key `memorySlots`,\r\n * wire view `{rev, count, openCount, slots[]}`); this plugin does NOT depend\r\n * on it — when the unit is not registered the client reads `undefined` from\r\n * `faceOf` and the section hides. This module is the read side of that\r\n * contract and is deliberately PURE — no React, no context, no IO — so the\r\n * narrowing can be tested directly.\r\n *\r\n * Forward compatibility: the shipped view carries `{id, title, kind, status,\r\n * priority}` only. A future prime-memory release may add `body`/`refs` to the\r\n * wire view (its projection plan keeps that door open); those fields are\r\n * narrowed opportunistically — present and well-formed → kept, absent or\r\n * malformed → dropped — so the section renders them without a brief-sidebar\r\n * change, and a malformed extra field can never blank the section.\r\n */\r\n\r\n/** The projection key `dsh-prime-memory` registers (its canonical constant). */\r\nexport const MEMORY_SLOTS_KEY = 'memorySlots'\r\n\r\n/** One slot of the wire view. */\r\nexport interface MemorySlotView {\r\n readonly id: string\r\n readonly title: string\r\n /** One of prime-memory's `SLOT_KINDS`: rule / todo / anchor / pointer. */\r\n readonly kind: string\r\n /** One of prime-memory's `SLOT_STATUSES`: open / done / dropped / expired. */\r\n readonly status: string\r\n /** 0–100; higher wins the activation competition. */\r\n readonly priority: number\r\n /** Slot body, once prime-memory exposes it on the wire (truncated at render). */\r\n readonly body?: string\r\n /** Reference targets, once prime-memory exposes them on the wire. */\r\n readonly refs?: readonly string[]\r\n /**\r\n * record_id 引用的展示解析(v0.5.0 契约:ref → `[type] 名称简述`),由\r\n * prime-memory 在投影帧构建时解析;路径/URL 类引用不产生条目。存在才收。\r\n */\r\n readonly refViews?: readonly SlotRefView[]\r\n}\r\n\r\n/** 一条 record_id 引用的展示解析(与 prime-memory 的 SlotRefView 同形)。 */\r\nexport interface SlotRefView {\r\n readonly ref: string\r\n readonly title: string\r\n}\r\n\r\n/** The whole wire view of one frame. */\r\nexport interface MemorySlotsView {\r\n readonly rev: number\r\n readonly count: number\r\n readonly openCount: number\r\n readonly slots: readonly MemorySlotView[]\r\n}\r\n\r\n/**\r\n * Narrow a raw projection value into a memory-slots view.\r\n *\r\n * Two outcomes are kept apart, mirroring the deliverables read side:\r\n * - `undefined` — the capability is ABSENT (prime-memory is not in the\r\n * composition, or no frame has carried the key yet), or the frame is\r\n * malformed beyond repair (a shape drift must not blank the whole tab);\r\n * - a view — the projection is live. A view with `slots: []` IS the empty\r\n * state: prime-memory publishes as soon as the unit registers, so \"present\r\n * but empty\" is a real value, not a sentinel.\r\n *\r\n * Malformed slot entries are dropped rather than thrown on (trusted-Host\r\n * value, defensive display), like `readTodos`.\r\n *\r\n * @param value - the raw `unknown` snapshot of the `memorySlots` face.\r\n * @returns the validated view, or `undefined` when the capability is absent.\r\n */\r\nexport function readSlots(value: unknown): MemorySlotsView | undefined {\r\n if (value === null || typeof value !== 'object' || Array.isArray(value)) return undefined\r\n const raw = value as Record<string, unknown>\r\n if (!isCounter(raw['rev']) || !isCounter(raw['count']) || !isCounter(raw['openCount'])) return undefined\r\n if (!Array.isArray(raw['slots'])) return undefined\r\n const slots: MemorySlotView[] = []\r\n for (const entry of raw['slots']) {\r\n const slot = readSlot(entry)\r\n if (slot !== undefined) slots.push(slot)\r\n }\r\n return { rev: raw['rev'], count: raw['count'], openCount: raw['openCount'], slots }\r\n}\r\n\r\n/** Whether a value is a non-negative safe integer (the view's counter shape). */\r\nfunction isCounter(value: unknown): value is number {\r\n return typeof value === 'number' && Number.isSafeInteger(value) && value >= 0\r\n}\r\n\r\n/** Narrow one slot entry; `undefined` = malformed, dropped. */\r\nfunction readSlot(value: unknown): MemorySlotView | undefined {\r\n if (value === null || typeof value !== 'object' || Array.isArray(value)) return undefined\r\n const raw = value as Record<string, unknown>\r\n if (typeof raw['id'] !== 'string' || raw['id'].length === 0) return undefined\r\n if (typeof raw['title'] !== 'string') return undefined\r\n if (typeof raw['kind'] !== 'string' || typeof raw['status'] !== 'string') return undefined\r\n if (typeof raw['priority'] !== 'number') return undefined\r\n const slot: { -readonly [K in keyof MemorySlotView]: MemorySlotView[K] } = {\r\n id: raw['id'],\r\n title: raw['title'],\r\n kind: raw['kind'],\r\n status: raw['status'],\r\n priority: raw['priority'],\r\n }\r\n if (typeof raw['body'] === 'string' && raw['body'].length > 0) slot.body = raw['body']\r\n if (Array.isArray(raw['refs'])) {\r\n const refs = raw['refs'].filter((ref): ref is string => typeof ref === 'string' && ref.length > 0)\r\n if (refs.length > 0) slot.refs = refs\r\n }\r\n if (Array.isArray(raw['refViews'])) {\r\n const views: SlotRefView[] = []\r\n for (const entry of raw['refViews']) {\r\n if (entry === null || typeof entry !== 'object' || Array.isArray(entry)) continue\r\n const view = entry as Record<string, unknown>\r\n if (typeof view['ref'] !== 'string' || view['ref'].length === 0) continue\r\n if (typeof view['title'] !== 'string' || view['title'].length === 0) continue\r\n views.push({ ref: view['ref'], title: view['title'] })\r\n }\r\n if (views.length > 0) slot.refViews = views\r\n }\r\n return slot\r\n}\r\n\r\n/** The body length at which the section truncates (full text rides the title tooltip). */\r\nexport const BODY_TRUNCATE_AT = 120\r\n\r\n/**\r\n * One-line truncation for a slot body. Already-short bodies pass through;\r\n * longer ones are cut at the limit with an ellipsis.\r\n */\r\nexport function truncateBody(body: string, limit = BODY_TRUNCATE_AT): string {\r\n const flat = body.replaceAll(/\\s+/g, ' ').trim()\r\n return flat.length <= limit ? flat : `${flat.slice(0, limit)}…`\r\n}\r\n","/**\r\n * Structural views of the `configForms` settings seam (DSH 0.1.7+).\r\n *\r\n * The settings card and the memory-section gate consume only these members of\r\n * the `ConfigForm` handle that `configForms.get(entryId)` returns, so the real\r\n * `@deepseek-ai/dsh-client-ui-settings/client` type graph is never imported —\r\n * the same reason `use-projection.ts` declares its own session faces and\r\n * `dock-shadow.tsx` its own slots face.\r\n *\r\n * `configForms` serves the STORED doc: a `.volatile()` field arrives as a\r\n * plain value here (the host unwraps the live refs before persistence), so\r\n * the client reads a plain config shape.\r\n */\r\n\r\n/** One snapshot of an entry's stored config. */\r\nexport interface SettingsScopeSnapshot<T> {\r\n status: 'loading' | 'ready' | 'unavailable'\r\n value: T | undefined\r\n revision: number | undefined\r\n /** Whether the current face may write (a read-only seat disables the controls). */\r\n writable: boolean\r\n}\r\n\r\n/** The `ConfigForm` handle face this plugin uses. */\r\nexport interface SettingsScope<T> {\r\n getSnapshot(): SettingsScopeSnapshot<T>\r\n subscribe(listener: () => void): () => void\r\n set(field: string, value: unknown): Promise<boolean>\r\n unset(field: string): Promise<boolean>\r\n}\r\n\r\n/** Structural view of the `configForms` cordis service, keyed by entry id. */\r\nexport interface ConfigFormsLike {\r\n get<T>(entryId: string): SettingsScope<T>\r\n}\r\n\r\n/**\r\n * This plugin's entry config, as the client reads it. Only the fields the\r\n * browser half consumes are declared; the schema (node half `Config`) owns\r\n * the full surface.\r\n */\r\nexport interface BriefClientConfig {\r\n /** Whether the summary tab renders the memory-slots section (default: on). */\r\n showMemorySlots?: boolean\r\n}\r\n\r\n/**\r\n * The switch's default. Applies wherever the config face cannot answer:\r\n * `configForms` absent, the scope not `ready`, or the field unset. The\r\n * section still only appears when the `memorySlots` projection exists, so\r\n * \"default on\" cannot paint anything on a host without prime-memory.\r\n */\r\nexport const DEFAULT_SHOW_MEMORY_SLOTS = true\r\n\r\n/**\r\n * Resolve the switch from one snapshot (pure; testable). Any\r\n * cannot-answer shape falls back to {@link DEFAULT_SHOW_MEMORY_SLOTS}; an\r\n * explicit `false` is the only off.\r\n */\r\nexport function resolveShowMemorySlots(\r\n snapshot: SettingsScopeSnapshot<BriefClientConfig> | undefined,\r\n): boolean {\r\n if (snapshot === undefined || snapshot.status !== 'ready') return DEFAULT_SHOW_MEMORY_SLOTS\r\n return snapshot.value?.showMemorySlots !== false\r\n}\r\n","/**\r\n * The memory-slots section: the `memorySlots` projection drawn as the third\r\n * part of the summary tab (fixed last: 进展 → 产物 → 记忆).\r\n *\r\n * TWO gates, both fail-open to \"hidden\", so a host without dsh-prime-memory\r\n * paints nothing here:\r\n *\r\n * 1. **The switch.** This plugin's entry config (`configForms` → entry\r\n * `dsh-brief-sidebar`, field `showMemorySlots`, default on). When the\r\n * config face cannot answer (service absent, scope not ready, field\r\n * unset) the default applies — see `scope-face.ts`.\r\n * 2. **The capability.** The `memorySlots` projection must exist for the\r\n * session; `undefined` hides the section entirely, exactly like the\r\n * optional deliverables section.\r\n *\r\n * Colours come from `--dsw-alias-*` tokens only (skin contract, enforced by\r\n * `tests/purity.spec.ts`).\r\n */\r\nimport { useCallback, useMemo, useSyncExternalStore } from 'react'\r\nimport { createElement } from 'react'\r\nimport type { CSSProperties, ReactNode } from 'react'\r\nimport type { Context } from '@deepseek-ai/cordis'\r\nimport {\r\n MEMORY_SLOTS_KEY,\r\n readSlots,\r\n truncateBody,\r\n} from './memory-slots'\r\nimport type { MemorySlotView, MemorySlotsView } from './memory-slots'\r\nimport { resolveShowMemorySlots } from '../scope-face'\r\nimport type { BriefClientConfig, ConfigFormsLike, SettingsScope, SettingsScopeSnapshot } from '../scope-face'\r\nimport { useProjectionValue } from '../use-projection'\r\nimport { expandCount } from '../locales'\r\n\r\nexport interface MemorySlotsSectionProps {\r\n /** The DSH locale lookup, passed down from `apply`. */\r\n t: (key: string) => string\r\n /** The client root context (the tab body's only way to reach services). */\r\n ctx?: Context\r\n /** The session this tab is scoped to. */\r\n scope?: { readonly sessionId?: string }\r\n}\r\n\r\nconst SECTION_STYLE: CSSProperties = {\r\n flexShrink: 0,\r\n borderBottom: '1px solid var(--dsw-alias-border-secondary)',\r\n}\r\n\r\nconst HEADER_STYLE: CSSProperties = {\r\n padding: '10px 12px 0',\r\n color: 'var(--dsw-alias-label-secondary)',\r\n fontSize: 12,\r\n fontWeight: 600,\r\n}\r\n\r\nconst LIST_STYLE: CSSProperties = {\r\n listStyle: 'none',\r\n margin: 0,\r\n padding: '6px 0',\r\n display: 'flex',\r\n flexDirection: 'column',\r\n}\r\n\r\nconst ROW_STYLE: CSSProperties = {\r\n display: 'flex',\r\n alignItems: 'flex-start',\r\n gap: 8,\r\n padding: '6px 12px',\r\n}\r\n\r\nconst TITLE_STYLE: CSSProperties = {\r\n minWidth: 0,\r\n overflowWrap: 'anywhere',\r\n color: 'var(--dsw-alias-label-secondary)',\r\n}\r\n\r\nconst META_STYLE: CSSProperties = {\r\n flexShrink: 0,\r\n color: 'var(--dsw-alias-label-tertiary)',\r\n fontSize: 11,\r\n lineHeight: '18px',\r\n whiteSpace: 'nowrap',\r\n}\r\n\r\n/** The kind pill: same form as the codeplan pill, tertiary emphasis. */\r\nconst KIND_STYLE: CSSProperties = {\r\n flexShrink: 0,\r\n color: 'var(--dsw-alias-label-tertiary)',\r\n border: '1px solid var(--dsw-alias-label-tertiary)',\r\n borderRadius: 999,\r\n padding: '0 6px',\r\n fontSize: 11,\r\n lineHeight: '16px',\r\n whiteSpace: 'nowrap',\r\n}\r\n\r\n/** The status pill: filled tint for an open slot, plain text otherwise. */\r\nconst STATUS_OPEN_STYLE: CSSProperties = {\r\n flexShrink: 0,\r\n color: 'var(--dsw-alias-label-primary)',\r\n border: '1px solid var(--dsw-alias-label-secondary)',\r\n borderRadius: 999,\r\n padding: '0 6px',\r\n fontSize: 11,\r\n lineHeight: '16px',\r\n whiteSpace: 'nowrap',\r\n}\r\n\r\nconst STATUS_SETTLED_STYLE: CSSProperties = {\r\n ...STATUS_OPEN_STYLE,\r\n color: 'var(--dsw-alias-label-tertiary)',\r\n border: '1px solid var(--dsw-alias-label-tertiary)',\r\n}\r\n\r\nconst BODY_STYLE: CSSProperties = {\r\n margin: 0,\r\n minWidth: 0,\r\n overflowWrap: 'anywhere',\r\n color: 'var(--dsw-alias-label-tertiary)',\r\n fontSize: 12,\r\n lineHeight: '18px',\r\n}\r\n\r\nconst REFS_STYLE: CSSProperties = {\r\n ...BODY_STYLE,\r\n fontSize: 11,\r\n}\r\n\r\nconst TOTAL_STYLE: CSSProperties = {\r\n padding: '6px 12px 10px',\r\n color: 'var(--dsw-alias-label-tertiary)',\r\n fontSize: 12,\r\n}\r\n\r\nconst EMPTY_STYLE: CSSProperties = {\r\n margin: '0 auto',\r\n padding: 24,\r\n maxWidth: 460,\r\n textAlign: 'center',\r\n color: 'var(--dsw-alias-label-tertiary)',\r\n lineHeight: 1.7,\r\n fontSize: 12,\r\n}\r\n\r\n/** Locale key per known slot status; unknown statuses fall back to the raw wire value. */\r\nconst STATUS_KEY: Record<string, string> = {\r\n open: 'slots.status.open',\r\n done: 'slots.status.done',\r\n dropped: 'slots.status.dropped',\r\n expired: 'slots.status.expired',\r\n}\r\n\r\n/**\r\n * One slot row: kind pill, title (full body rides the tooltip), status pill,\r\n * priority; body line underneath when the wire view carries it; refs as\r\n * resolved 名称简述 (record_id → title) with the raw refs on the tooltip, or\r\n * raw refs when the host has not resolved them.\r\n */\r\nfunction SlotRow(props: { slot: MemorySlotView; t: (key: string) => string }): ReactNode {\r\n const { slot, t } = props\r\n const statusLabel = STATUS_KEY[slot.status] === undefined ? slot.status : t(STATUS_KEY[slot.status])\r\n const title = slot.body === undefined ? slot.title : `${slot.title} — ${slot.body}`\r\n const refsLine = slot.refViews !== undefined\r\n ? slot.refViews.map((view) => view.title).join(' · ')\r\n : slot.refs !== undefined\r\n ? slot.refs.join(' · ')\r\n : undefined\r\n return createElement(\r\n 'li',\r\n { style: ROW_STYLE },\r\n createElement('span', { style: KIND_STYLE }, slot.kind),\r\n createElement(\r\n 'div',\r\n { style: { minWidth: 0, flex: '1 1 auto', display: 'flex', flexDirection: 'column', gap: 2 } },\r\n createElement('span', { style: TITLE_STYLE, title }, slot.title),\r\n slot.body !== undefined && createElement('p', { style: BODY_STYLE }, truncateBody(slot.body)),\r\n refsLine !== undefined\r\n && createElement(\r\n 'p',\r\n {\r\n style: REFS_STYLE,\r\n title: slot.refs !== undefined ? slot.refs.join('\\n') : undefined,\r\n },\r\n refsLine,\r\n ),\r\n ),\r\n createElement('span', { style: META_STYLE }, `P${slot.priority}`),\r\n createElement(\r\n 'span',\r\n { style: slot.status === 'open' ? STATUS_OPEN_STYLE : STATUS_SETTLED_STYLE },\r\n statusLabel,\r\n ),\r\n )\r\n}\r\n\r\n/**\r\n * A no-op unsubscribe source, kept as one identity so the absent-scope\r\n * subscription never thrashes (returns a no-op disposer, as the store\r\n * contract requires).\r\n */\r\nconst NO_SUBSCRIBE = (): (() => void) => () => {}\r\n\r\n/**\r\n * The snapshot served while no config scope is reachable — resolves to the\r\n * switch default (on) in `resolveShowMemorySlots`.\r\n */\r\nconst ABSENT_SNAPSHOT: SettingsScopeSnapshot<BriefClientConfig> = {\r\n status: 'unavailable',\r\n value: undefined,\r\n revision: undefined,\r\n writable: false,\r\n}\r\n\r\n/**\r\n * Subscribe to this plugin's entry config and resolve the section switch.\r\n *\r\n * The scope is resolved defensively (a host without `configForms` degrades to\r\n * the default instead of throwing inside a render) and subscribed with\r\n * `useSyncExternalStore`, so flipping the switch in settings updates the open\r\n * tab without a reload.\r\n *\r\n * @param ctx - the client root context handed to the tab body.\r\n * @returns whether the memory-slots section may render at all.\r\n */\r\nexport function useShowMemorySlots(ctx: Context | undefined): boolean {\r\n const scope = useMemo(() => {\r\n if (ctx === undefined) return undefined\r\n const configForms = ctx.get('configForms') as ConfigFormsLike | undefined\r\n if (configForms === undefined || typeof configForms.get !== 'function') return undefined\r\n return configForms.get<BriefClientConfig>('dsh-brief-sidebar')\r\n }, [ctx])\r\n\r\n const subscribe = useCallback(\r\n (listener: () => void) => scope?.subscribe(listener) ?? NO_SUBSCRIBE(),\r\n [scope],\r\n )\r\n const getSnapshot = useCallback(\r\n () => scope?.getSnapshot() ?? ABSENT_SNAPSHOT,\r\n [scope],\r\n )\r\n\r\n return resolveShowMemorySlots(useSyncExternalStore(subscribe, getSnapshot, getSnapshot))\r\n}\r\n\r\n/**\r\n * The memory-slots section. Both hooks run unconditionally (a conditional\r\n * hook order would break the moment the switch flips); the switch only\r\n * decides whether the resolved view paints.\r\n * @param props - translation, client context, session scope.\r\n */\r\nexport function MemorySlotsSection(props: MemorySlotsSectionProps): ReactNode {\r\n const { t, ctx, scope } = props\r\n\r\n const show = useShowMemorySlots(ctx)\r\n const view: MemorySlotsView | undefined = readSlots(\r\n useProjectionValue(ctx, scope?.sessionId, MEMORY_SLOTS_KEY),\r\n )\r\n\r\n // Switched off at the settings faces — the section is willed away, not\r\n // empty (an empty state would claim prime-memory answered when the user\r\n // simply closed the section).\r\n if (show === false) return null\r\n\r\n // Capability absent (prime-memory not in the composition, no frame yet) —\r\n // hide, don't nag; the memory panel remains the write surface.\r\n if (view === undefined) return null\r\n\r\n if (view.slots.length === 0) {\r\n return createElement(\r\n 'div',\r\n { style: SECTION_STYLE, 'data-dsh-brief-sidebar': 'memory-slots-section' },\r\n createElement('div', { style: HEADER_STYLE }, t('section.memorySlots')),\r\n createElement(\r\n 'div',\r\n { style: EMPTY_STYLE },\r\n createElement('div', { style: { marginBottom: 6, color: 'var(--dsw-alias-label-secondary)' } }, t('slots.empty')),\r\n createElement('div', null, t('slots.emptyHint')),\r\n ),\r\n )\r\n }\r\n\r\n return createElement(\r\n 'div',\r\n { style: SECTION_STYLE, 'data-dsh-brief-sidebar': 'memory-slots-section' },\r\n createElement('div', { style: HEADER_STYLE }, t('section.memorySlots')),\r\n createElement(\r\n 'ul',\r\n { style: LIST_STYLE },\r\n view.slots.map(slot => createElement(SlotRow, { key: slot.id, slot, t })),\r\n ),\r\n createElement(\r\n 'div',\r\n { style: TOTAL_STYLE },\r\n expandCount(t('slots.openCount'), view.openCount),\r\n ),\r\n )\r\n}\r\n","/**\r\n * The summary tab body: the session's at-a-glance state, composed of sections.\r\n *\r\n * Section order: 进展 (the todos board) → 产物 (produced files, codeplan\r\n * artifacts marked inline) → 记忆 (the active memory slots of the session,\r\n * served by the `memorySlots` projection dsh-prime-memory registers).\r\n * Each section owns its own data chain and its own gating contract; this\r\n * component owns only the shell.\r\n *\r\n * Two better-sidebar contracts are honoured:\r\n *\r\n * 1. **Height contract.** The tab body mounts inside a full-height column flex\r\n * host whose `.paneBody` is a definite-height BLOCK scroll container. The\r\n * root declares `height: 100%` + `min-height: 0`, and the scrolling element\r\n * is an inner div — not the root. Sections are non-scrolling slices with a\r\n * bottom border; the inner div scrolls them all.\r\n * 2. **`visible` pause.** Nothing is drawn while the tab is not the active\r\n * one, so a hidden tab does not re-render on every projection frame.\r\n */\r\nimport { createElement } from 'react'\r\nimport type { CSSProperties, ReactNode } from 'react'\r\nimport type { Context } from '@deepseek-ai/cordis'\r\nimport { TodoSection } from './TodoBoardTab'\r\nimport type { TodoScope } from './TodoBoardTab'\r\nimport { DeliverablesSection } from './summary/DeliverablesSection'\r\nimport { MemorySlotsSection } from './summary/MemorySlotsSection'\r\n\r\nexport interface SummaryTabProps {\r\n /** The DSH locale lookup, passed down from `apply`. */\r\n t: (key: string) => string\r\n /** The client root context (the tab body's only way to reach services). */\r\n ctx?: Context\r\n /** The session this tab is scoped to. */\r\n scope?: TodoScope\r\n /** Whether the tab is the active one AND the panel is open. */\r\n visible?: boolean\r\n}\r\n\r\nconst ROOT_STYLE: CSSProperties = {\r\n display: 'flex',\r\n flexDirection: 'column',\r\n height: '100%',\r\n minHeight: 0,\r\n background: 'var(--dsw-alias-bg-layer-1)',\r\n color: 'var(--dsw-alias-label-primary)',\r\n font: 'inherit',\r\n fontSize: 13,\r\n}\r\n\r\nconst SCROLL_STYLE: CSSProperties = { flex: 1, minHeight: 0, overflow: 'auto' }\r\n\r\n/**\r\n * The summary tab.\r\n * @param props - translation, client context, session scope, visibility.\r\n */\r\nexport function SummaryTab(props: SummaryTabProps): ReactNode {\r\n const { t, ctx, scope, visible = true } = props\r\n\r\n // A hidden tab renders NOTHING — not even the shell — so it never\r\n // re-renders on a projection frame while it is off screen.\r\n if (!visible) return null\r\n\r\n return createElement(\r\n 'div',\r\n { style: ROOT_STYLE, 'data-dsh-brief-sidebar': 'board' },\r\n createElement('div', { style: SCROLL_STYLE },\r\n createElement(TodoSection, { key: 'progress', t, ctx, scope }),\r\n createElement(DeliverablesSection, { key: 'deliverables', t, ctx, scope }),\r\n // 记忆分区(固定最底):入口开关(configForms `showMemorySlots`,默认开)\r\n // 与能力存在(dsh-prime-memory 的 `memorySlots` 投影)二者都满足才渲染,\r\n // 缺一即整段隐藏 —— 门控逻辑收敛在 MemorySlotsSection 内。\r\n createElement(MemorySlotsSection, { key: 'memory-slots', t, ctx, scope }),\r\n ),\r\n )\r\n}\r\n","/**\r\n * Tab glyph for the task board.\r\n *\r\n * Skin contract: plugin-drawn glyphs take their colour from the `--dsw-alias-*`\r\n * tokens — no colour literals anywhere in this file.\r\n */\r\nimport type { ReactNode } from 'react'\r\n\r\n/**\r\n * A checklist mark: three rows with ticks and text rails — the same status\r\n * vocabulary the board paints.\r\n * @param size - square edge in px (the host passes its own tab-icon size).\r\n */\r\nexport function BriefIcon(size: number): ReactNode {\r\n return (\r\n <svg\r\n width={size}\r\n height={size}\r\n viewBox=\"0 0 16 16\"\r\n fill=\"none\"\r\n aria-hidden=\"true\"\r\n focusable=\"false\"\r\n style={{ display: 'block', flex: 'none' }}\r\n >\r\n <rect\r\n x={1.6}\r\n y={2.4}\r\n width={12.8}\r\n height={11.2}\r\n rx={2}\r\n stroke=\"var(--dsw-alias-label-tertiary)\"\r\n strokeWidth={1.2}\r\n />\r\n <path\r\n d=\"M4.2 6.1l1 1 1.8-2\"\r\n stroke=\"var(--dsw-alias-state-success-primary)\"\r\n strokeWidth={1.3}\r\n strokeLinecap=\"round\"\r\n strokeLinejoin=\"round\"\r\n />\r\n <path\r\n d=\"M4.2 10.6l1 1 1.8-2\"\r\n stroke=\"var(--dsw-alias-label-tertiary)\"\r\n strokeWidth={1.3}\r\n strokeLinecap=\"round\"\r\n strokeLinejoin=\"round\"\r\n />\r\n <path\r\n d=\"M8.4 6.4h4M8.4 10.9h4\"\r\n stroke=\"var(--dsw-alias-label-tertiary)\"\r\n strokeWidth={1.2}\r\n strokeLinecap=\"round\"\r\n />\r\n </svg>\r\n )\r\n}\r\n","/**\r\n * Hiding the official todo dock.\r\n *\r\n * `conversation.input.dock` is a LIST slot, and its cells are keyed by each\r\n * entry's `id`. The slot core keeps one live entry per cell — entries are\r\n * sorted by `priority` ascending (then by `order`) and the first live entry of\r\n * a cell is the one the outlet renders, so a SECOND registration with the same\r\n * `id` at a LOWER priority shadows the first. The official todo entry\r\n * (`conversation-todo-dock` in `dsh-client-ui-conversation`) registers\r\n * `{ id: 'todo', order: 0 }` with the default priority 0, so\r\n * `{ id: 'todo', priority: -1 }` wins the cell and the official panel never\r\n * renders while this plugin is mounted.\r\n *\r\n * This is the same mechanism `dsh-input-traffic` uses on the sibling `queue`\r\n * cell (`{ id: 'queue', priority: -1 }`), so it is a proven seam on this host\r\n * generation, not a guess.\r\n *\r\n * Binding to a built-in id is a real coupling: if upstream renames the cell,\r\n * the shadow silently stops winning and the official dock simply reappears\r\n * (fail-open — the data is still shown, just twice). `slots.inject` is used\r\n * rather than a bare `register` because the slot is declared by a built-in\r\n * entry whose lifetime we do not control: `inject` waits for the declaration\r\n * and re-installs the entry if the declaration is ever recreated.\r\n *\r\n * Renders nothing on purpose: the todo UI now lives in this plugin's sidebar\r\n * tab. Return a summary chip here instead to keep a one-line entry point in the\r\n * composer band.\r\n */\r\nimport type { ReactNode } from 'react'\r\nimport type { Context } from '@deepseek-ai/cordis'\r\n\r\n/** The built-in cell id of the official todo dock. */\r\nexport const TODO_CELL_ID = 'todo'\r\n\r\n/** Lower than the built-in's default 0 — the winner of the `todo` cell. */\r\nexport const TODO_SHADOW_PRIORITY = -1\r\n\r\n/** Structural view of the client slots service (avoids a second type graph). */\r\nexport interface SlotsLike {\r\n inject(key: string, callback: () => (() => void) | undefined): () => void\r\n register(options: Record<string, unknown>, component: (props: unknown) => ReactNode): () => void\r\n}\r\n\r\n/** The replacement entry: deliberately empty. */\r\nfunction TodoDockShadow(): ReactNode {\r\n return null\r\n}\r\n\r\n/**\r\n * Register the shadowing entry.\r\n *\r\n * The caller wraps this in `ctx.effect(...)` so the returned disposer rides the\r\n * plugin fiber: unloading the plugin restores the official dock.\r\n *\r\n * @param ctx - the client root context.\r\n * @returns the disposer, or undefined when the slots service is unreachable.\r\n */\r\nexport function registerTodoDockShadow(ctx: Context): (() => void) | undefined {\r\n const slots = ctx.get('slots') as SlotsLike | undefined\r\n if (slots === undefined || typeof slots.inject !== 'function') return undefined\r\n\r\n return slots.inject('conversation.input.dock', () =>\r\n slots.register(\r\n {\r\n name: 'conversation.input.dock',\r\n id: TODO_CELL_ID,\r\n order: 0,\r\n priority: TODO_SHADOW_PRIORITY,\r\n registrant: 'dsh-brief-sidebar',\r\n },\r\n TodoDockShadow,\r\n ),\r\n )\r\n}\r\n","/**\r\n * BriefSettingsCard — the brief-sidebar settings card.\r\n *\r\n * One component mounted at TWO seats, fed by the SAME inject factory (one\r\n * source of truth, the dsh-thinking-levels arrangement):\r\n *\r\n * - the plugin-family settings section (「起子插件设置」, the `settings.section`\r\n * entry dsh-thinking-levels registers; this plugin contributes its card to\r\n * the `dsh-family.tab` child slot — the inject idles harmlessly when the\r\n * family section is absent);\r\n * - the Plugins-page configuration card (`plugins.bundle.config`, keyed by\r\n * the package name, rendered on the bundle's detail page).\r\n *\r\n * The card binds this plugin's entry config through the `configForms` service\r\n * and renders the fields the summary tab reads. Every change commits\r\n * immediately through the scope (no staged form), so a flip applies to the\r\n * open summary tab on its next render.\r\n *\r\n * The `t` binder is injected by the factory (the same closure the tab uses) —\r\n * the card does not rely on the framework's locale props, so it renders the\r\n * active language on every host generation. Kept dependency-free beyond\r\n * react: inline styles over `--dsw-alias-*` tokens only (no CSS modules, no\r\n * hex/rgba literals — the purity spec enforces the token contract).\r\n */\r\nimport { useCallback, useState, useSyncExternalStore } from 'react'\r\nimport { createElement } from 'react'\r\nimport type { CSSProperties, ChangeEvent, ReactNode } from 'react'\r\nimport type { BriefClientConfig, SettingsScope, SettingsScopeSnapshot } from './scope-face'\r\n\r\n/** A no-op unsubscribe source, kept as one identity so effects never thrash. */\r\nconst NO_SUBSCRIBE = (): (() => void) => () => {}\r\n\r\n/** The snapshot served while no scope is reachable. */\r\nconst ABSENT: SettingsScopeSnapshot<BriefClientConfig> = {\r\n status: 'unavailable',\r\n value: undefined,\r\n revision: undefined,\r\n writable: false,\r\n}\r\n\r\n/** Injected face: the entry scope, plus the plugin's own binder. */\r\nexport interface BriefSettingsCardInjected {\r\n /** `configForms.get('dsh-brief-sidebar')` — may be unavailable on odd hosts. */\r\n scope?: SettingsScope<BriefClientConfig>\r\n /** The plugin's locale binder (applied-time closure over the locale service). */\r\n t: (key: string) => string\r\n}\r\n\r\nexport type BriefSettingsCardProps = BriefSettingsCardInjected\r\n\r\n/* ── shared row styling (inline; keeps the client bundle CSS-free) ──────── */\r\n\r\nconst CARD_STYLE: CSSProperties = {\r\n border: '1px solid var(--dsw-alias-border-l2)',\r\n background: 'var(--dsw-alias-bg-layer-3)',\r\n borderRadius: 12,\r\n transition: 'border-color 0.16s, background 0.16s',\r\n}\r\n\r\nconst HEADER_STYLE: CSSProperties = {\r\n appearance: 'none',\r\n width: '100%',\r\n font: 'inherit',\r\n color: 'inherit',\r\n textAlign: 'left',\r\n cursor: 'pointer',\r\n background: 'none',\r\n border: 0,\r\n borderRadius: 12,\r\n display: 'flex',\r\n alignItems: 'center',\r\n gap: 12,\r\n padding: '14px 16px',\r\n}\r\n\r\nconst HEADER_TEXT_STYLE: CSSProperties = { flex: '1 1 0%', minWidth: 0 }\r\n\r\nconst NAME_STYLE: CSSProperties = {\r\n fontSize: 14,\r\n fontWeight: 600,\r\n color: 'var(--dsw-alias-label-primary)',\r\n}\r\n\r\nconst DESC_STYLE: CSSProperties = {\r\n color: 'var(--dsw-alias-label-tertiary)',\r\n fontSize: 13,\r\n lineHeight: 1.5,\r\n}\r\n\r\nconst CHEVRON_STYLE = (open: boolean): CSSProperties => ({\r\n color: 'var(--dsw-alias-label-tertiary)',\r\n flex: '0 0 auto',\r\n transition: 'transform 0.16s',\r\n transform: open ? 'rotate(180deg)' : 'none',\r\n})\r\n\r\nconst BODY_STYLE: CSSProperties = { padding: '12px 16px' }\r\n\r\nconst ROW_STYLE: CSSProperties = {\r\n display: 'flex',\r\n alignItems: 'flex-start',\r\n justifyContent: 'space-between',\r\n gap: 12,\r\n padding: '8px 0',\r\n fontSize: 13,\r\n lineHeight: '20px',\r\n}\r\n\r\nconst ROW_TEXT_STYLE: CSSProperties = {\r\n display: 'flex',\r\n flexDirection: 'column',\r\n gap: 4,\r\n minWidth: 0,\r\n}\r\n\r\nconst LABEL_STYLE: CSSProperties = { margin: 0, color: 'var(--dsw-alias-label-primary)' }\r\n\r\nconst HINT_STYLE: CSSProperties = {\r\n margin: 0,\r\n fontSize: 12,\r\n lineHeight: '18px',\r\n color: 'var(--dsw-alias-label-tertiary)',\r\n}\r\n\r\nconst SWITCH_STYLE: CSSProperties = { flexShrink: 0, margin: 0, cursor: 'pointer' }\r\n\r\n/**\r\n * The settings card body. `scope` is optional on purpose: the inject factory\r\n * always provides it, but a host whose `configForms` face degrades must not\r\n * crash the settings page — the card renders the unavailable note and keeps\r\n * its header.\r\n */\r\nexport function BriefSettingsCard(props: BriefSettingsCardProps): ReactNode {\r\n const { t, scope } = props\r\n const [open, setOpen] = useState(true)\r\n\r\n const snapshot: SettingsScopeSnapshot<BriefClientConfig> | undefined = useSyncExternalStore(\r\n useCallback((listener: () => void) => scope?.subscribe(listener) ?? NO_SUBSCRIBE(), [scope]),\r\n useCallback(() => scope?.getSnapshot() ?? ABSENT, [scope]),\r\n useCallback(() => scope?.getSnapshot() ?? ABSENT, [scope]),\r\n )\r\n const unavailable = scope === undefined || snapshot.status === 'unavailable'\r\n const readonly = unavailable || !snapshot.writable\r\n const checked = snapshot.value?.showMemorySlots !== false\r\n\r\n return createElement(\r\n 'div',\r\n { style: CARD_STYLE, 'data-dsh-brief-sidebar': 'settings-card' },\r\n createElement(\r\n 'button',\r\n { type: 'button', 'aria-expanded': open, style: HEADER_STYLE, onClick: () => { setOpen(current => !current) } },\r\n createElement(\r\n 'span',\r\n { style: HEADER_TEXT_STYLE },\r\n createElement('div', { style: NAME_STYLE }, t('settings.title')),\r\n createElement('div', { style: DESC_STYLE }, t('settings.desc')),\r\n ),\r\n createElement(\r\n 'svg',\r\n { width: 16, height: 16, viewBox: '0 0 16 16', 'aria-hidden': true, style: CHEVRON_STYLE(open) },\r\n createElement('path', {\r\n d: 'M4 6l4 4 4-4',\r\n fill: 'none',\r\n stroke: 'currentColor',\r\n strokeWidth: 1.5,\r\n strokeLinecap: 'round',\r\n strokeLinejoin: 'round',\r\n }),\r\n ),\r\n ),\r\n open && createElement(\r\n 'div',\r\n { style: BODY_STYLE },\r\n unavailable\r\n ? createElement('p', { style: HINT_STYLE }, t('settings.unavailable'))\r\n : createElement(\r\n 'div',\r\n { style: ROW_STYLE },\r\n createElement(\r\n 'div',\r\n { style: ROW_TEXT_STYLE },\r\n createElement('label', { htmlFor: 'dsh-brief-sidebar-show-memory-slots', style: LABEL_STYLE }, t('settings.memorySlots')),\r\n createElement('p', { style: HINT_STYLE }, t('settings.memorySlotsHint')),\r\n ),\r\n createElement('input', {\r\n id: 'dsh-brief-sidebar-show-memory-slots',\r\n type: 'checkbox',\r\n checked,\r\n disabled: readonly,\r\n style: SWITCH_STYLE,\r\n onChange: (event: ChangeEvent<HTMLInputElement>) => {\r\n void scope?.set('showMemorySlots', event.currentTarget.checked)\r\n },\r\n }),\r\n ),\r\n !unavailable && !snapshot.writable && createElement('p', { style: HINT_STYLE }, t('settings.readonly')),\r\n ),\r\n )\r\n}\r\n","/**\r\n * Browser half: the task board tab, plus the shadow that hides the official\r\n * composer-band todo panel.\r\n *\r\n * Registration order matters and is deliberate:\r\n *\r\n * 1. **Dictionaries first.** Every later registration can render translated\r\n * copy, and a dictionary that lands after the first paint would show raw\r\n * keys for a frame.\r\n * 2. **Soft-dependency check.** `inject` already waits for `betterSidebar`, so\r\n * the guard only fires in a composition that relaxes the inject list. It is\r\n * kept because it is what stops the plugin from hiding the official panel\r\n * when it has no board to replace it with.\r\n * 3. **Shadow the official dock.** Deliberately BEFORE the tab registration:\r\n * the panel is the only other carrier of the list, so the replacement has to\r\n * exist in the same activation.\r\n * 4. **Register the tab.**\r\n * 5. **Register the settings cards.** The memory-section switch gets TWO\r\n * faces from ONE component + ONE inject factory: a card in the plugin-family\r\n * settings section (「起子插件设置」, hosted by dsh-thinking-levels) and the\r\n * Plugins-page configuration card. `configForms` resolves through a\r\n * deferred inject (the service may start after apply — the same lateness\r\n * the composer model panel in dsh-thinking-levels guards against); on a\r\n * host without it the cards never register and the section gate falls back\r\n * to its default (on).\r\n *\r\n * Every registration rides `ctx.effect(fn, label)` so its disposer is revoked on\r\n * fiber teardown (HMR / disable), which is also what makes the shadow\r\n * reversible: unload the plugin and the official dock renders again. The\r\n * settings cards are the family-proven exception: they ride the deferred\r\n * `configForms` inject whose disposers are not held (same as\r\n * dsh-thinking-levels' composer panel registration).\r\n *\r\n * Contract notes for the two surfaces touched here:\r\n * - better-sidebar tabs are hosted by DSH's native right Sidebar, so the tab\r\n * body receives `TabComponentProps` (`ctx` + `scope` + `visible`) and NOT the\r\n * framework's session-scoped slot props.\r\n * - `conversation.input.dock` is the DSH slot the official todo panel occupies;\r\n * see `brief/dock-shadow.tsx` for why a lower priority wins the cell.\r\n */\r\nimport { createElement } from 'react'\r\nimport type { ReactNode } from 'react'\r\nimport type { Context } from '@deepseek-ai/cordis'\r\nimport type { BetterSidebarService, TabComponentProps } from 'dsh-better-sidebar/client/service'\r\nimport { SummaryTab } from './SummaryTab'\r\nimport { BriefIcon } from './BriefIcon'\r\nimport { NS, dictionaries } from './locales'\r\nimport { readTodos, unfinishedCount } from './brief/board'\r\nimport { registerTodoDockShadow } from './brief/dock-shadow'\r\nimport type { SlotsLike } from './brief/dock-shadow'\r\nimport { TODOS_KEY } from './brief/use-todos'\r\nimport { projectionSnapshot } from './use-projection'\r\nimport { BriefSettingsCard } from './settings-card'\r\nimport type { BriefSettingsCardProps } from './settings-card'\r\nimport type { BriefClientConfig, ConfigFormsLike } from './scope-face'\r\n\r\n/**\r\n * Tab type id. Package-prefixed so it cannot collide with a built-in type.\r\n * Unchanged since the 0.1.0 board tab: the summary tab REPLACES the board tab\r\n * in place, so an opened or pinned tab survives the upgrade.\r\n */\r\nexport const TAB_ID = 'dsh-brief-sidebar:board'\r\n\r\n/**\r\n * Position in the host's new-tab guide.\r\n *\r\n * Built-ins on this host generation sit at editor 10 / git 20 / subagent 30 /\r\n * sidechat 35 / terminal 40 / browser 50. 15 places a per-session task board\r\n * right after the editor, which is where a reader looks for session state.\r\n */\r\nexport const TAB_ORDER = 15\r\n\r\n/** Services required before `apply` runs. */\r\n// Generation-neutral list only: betterSidebar/locale ride ctx.get soft-reads in\n// apply (both absent on 0.1.0/0.1.1 — declaring them pends the whole client\n// entry, web boot refuses to render; playbook §1).\nexport const inject = ['slots']\r\n\r\n/**\r\n * Structural view of the DSH locale service — only the two members this plugin\r\n * calls. Declared locally on purpose: importing the real type would pull the\r\n * whole client type graph into a browser-only plugin for two signatures.\r\n */\r\ninterface LocaleLike {\r\n register(ns: string, dicts: Record<string, Record<string, string>>): () => void\r\n bind(ns: string): (key: string) => string\r\n}\r\n\r\n/**\r\n * An effect body must return a disposer (cordis rejects `undefined` with a\r\n * `TypeError`), so a path that could not register anything returns this no-op\r\n * instead of nothing.\r\n */\r\nconst NOOP = (): void => {}\r\n\r\n/**\r\n * Browser-face apply.\r\n *\r\n * @param ctx - the client root context.\r\n */\r\nexport function apply(ctx: Context): void {\r\n // 1. Plugin-owned dictionaries, in one registration.\r\n const locale: LocaleLike | undefined = ctx.get('locale')\r\n if (locale !== undefined) {\r\n ctx.effect(() => locale.register(NS, dictionaries), 'dsh-brief-sidebar: dictionaries')\r\n }\r\n\r\n /**\r\n * Translate one of our keys. `bind` returns a cached function that reads the\r\n * ACTIVE locale at call time, so a language switch needs no re-apply; an\r\n * unknown namespace or key falls back to the key itself rather than throwing\r\n * inside a render.\r\n */\r\n const t = (key: string): string => {\r\n if (locale === undefined) return key\r\n try {\r\n return locale.bind(NS)(key) || key\r\n } catch {\r\n return key\r\n }\r\n }\r\n\r\n // 2. Soft dependency: without better-sidebar there is no place to show the\r\n // list, so the plugin stays inert and the official panel keeps rendering.\r\n const bar: BetterSidebarService | undefined = ctx.get('betterSidebar')\r\n if (bar === undefined) return\r\n\r\n // 3. Hide the official composer-band panel (reversible on unload).\r\n ctx.effect(() => registerTodoDockShadow(ctx) ?? NOOP, 'dsh-brief-sidebar: dock shadow')\r\n\r\n // 4. The board.\r\n ctx.effect(\r\n () =>\r\n bar.registerTab({\r\n id: TAB_ID,\r\n title: () => t('tab.title'),\r\n description: () => t('tab.desc'),\r\n icon: (size: number) => BriefIcon(size),\r\n order: TAB_ORDER,\r\n // Shorthand for `dedupeKey: () => id`: reopening focuses the existing tab.\r\n single: true,\r\n /**\r\n * Unfinished count on the tab strip. Called on every tab-bar render, so\r\n * it reads one snapshot and allocates no subscription; a completed list\r\n * shows no badge at all.\r\n */\r\n badge: (badgeCtx, badgeScope) => {\r\n const todos = readTodos(projectionSnapshot(badgeCtx, badgeScope.sessionId, TODOS_KEY))\r\n if (todos === undefined) return null\r\n const pending = unfinishedCount(todos)\r\n return pending > 0 ? pending : null\r\n },\r\n component: (props: TabComponentProps) =>\r\n createElement(SummaryTab, {\r\n t,\r\n ctx: props.ctx,\r\n scope: props.scope,\r\n visible: props.visible,\r\n }),\r\n }),\r\n 'dsh-brief-sidebar: tab',\r\n )\r\n\r\n // 5. The settings cards (see the module doc): ONE component + ONE inject\r\n // factory at TWO seats. The entry ids follow the family convention —\r\n // `dsh-family.tab` contributors key by short id (session-guard →\r\n // 'session-guard'), `plugins.bundle.config` keys by package name.\r\n // `configForms` rides a deferred inject: the service lives in the\r\n // settings module, which may start after this apply; the callback simply\r\n // never fires on a host without it (the gate then reads its default).\r\n const slots = ctx.get('slots') as SlotsLike | undefined\r\n ;(ctx as unknown as {\r\n inject: (deps: string[], cb: (scope: { configForms?: ConfigFormsLike }) => void) => void\r\n }).inject(['configForms'], (scope) => {\r\n const configForms = scope.configForms\r\n if (slots === undefined || configForms === undefined || typeof configForms.get !== 'function') return\r\n\r\n /** Same face for both seats: the entry scope plus the applied-time binder. */\r\n const injectFactory = (): BriefSettingsCardProps => ({\r\n t,\r\n scope: configForms.get<BriefClientConfig>('dsh-brief-sidebar'),\r\n })\r\n /** The slot core hands the component `unknown` props (registry typing). */\r\n const component = BriefSettingsCard as unknown as (props: unknown) => ReactNode\r\n\r\n slots.inject('dsh-family.tab', () =>\r\n slots.register(\r\n {\r\n name: 'dsh-family.tab',\r\n id: 'brief-sidebar',\r\n order: 30,\r\n label: () => t('settings.title'),\r\n inject: injectFactory,\r\n },\r\n component,\r\n ),\r\n )\r\n slots.inject('plugins.bundle.config', () =>\r\n slots.register(\r\n {\r\n name: 'plugins.bundle.config',\r\n key: 'dsh-brief-sidebar',\r\n inject: injectFactory,\r\n },\r\n component,\r\n ),\r\n )\r\n })\r\n}\r\n"],"mappings":"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"}
1
+ {"version":3,"file":"client.js","names":["STATUS_KEY","NO_SUBSCRIBE","ABSENT","useMemo","useCallback","useSyncExternalStore","useMemo","SECTION_STYLE","HEADER_STYLE","LIST_STYLE","ROW_STYLE","createElement","SECTION_STYLE","HEADER_STYLE","LIST_STYLE","ROW_STYLE","CHEVRON_STYLE","createElement","useState","HEADER_STYLE","ROW_STYLE","BODY_STYLE","createElement","NO_SUBSCRIBE","useMemo","useCallback","useSyncExternalStore","createElement","useState","useSyncExternalStore","useCallback","createElement","createElement"],"sources":["../src/client/brief/board.ts","../src/client/use-projection.ts","../src/client/brief/use-todos.ts","../src/client/TodoBoardTab.tsx","../src/projection/keys.ts","../src/client/file-open.ts","../src/client/summary/deliverables.ts","../src/client/locales.ts","../src/client/summary/DeliverablesSection.tsx","../src/client/summary/memory-slots.ts","../src/client/scope-face.ts","../src/client/summary/MemorySlotsSection.tsx","../src/client/SummaryTab.tsx","../src/client/native-sidebar.tsx","../src/client/BriefIcon.tsx","../src/client/brief/dock-shadow.tsx","../src/client/settings-card.tsx","../src/client/index.tsx"],"sourcesContent":["/**\r\n * The `todos` projection — reading and classifying it.\r\n *\r\n * The Host computes this projection (`dsh-tool-todo` registers the key) and\r\n * pushes finished whole values; the client never folds it. This module is the\r\n * read side of that contract and is deliberately PURE — no React, no context,\r\n * no IO — so the mapping can be tested directly.\r\n */\r\n/** The projection's three-state lifecycle (`TodoItem` in `dsh-tool-todo`). */\r\nexport type TodoStatus = 'pending' | 'in_progress' | 'completed'\r\n\r\n/** One entry of the `todos` projection. */\r\nexport interface TodoItem {\r\n readonly content: string\r\n readonly status: TodoStatus\r\n}\r\n\r\nconst STATUSES: readonly TodoStatus[] = ['pending', 'in_progress', 'completed']\r\n\r\n/** Locale key per status. Kept here so this module owns the vocabulary. */\r\nconst STATUS_KEY: Record<TodoStatus, string> = {\r\n pending: 'status.pending',\r\n in_progress: 'status.inProgress',\r\n completed: 'status.completed',\r\n}\r\n\r\n/** Locale key for one status label. */\r\nexport function statusKey(status: TodoStatus): string {\r\n return STATUS_KEY[status]\r\n}\r\n\r\n/**\r\n * Narrow a raw projection value into a todo list.\r\n *\r\n * Three outcomes are kept apart on purpose, mirroring the store's own contract:\r\n * - `undefined` — the capability is ABSENT (no session, the host unit is\r\n * unmounted, or no baseline/frame has carried the key yet);\r\n * - `[]` — the projection is present and the list is empty (`null` on the wire);\r\n * - a list — real entries.\r\n *\r\n * A malformed entry is dropped rather than thrown on: the projection is a\r\n * trusted Host value, but a shape drift must not blank the whole board.\r\n *\r\n * @param value - the raw `unknown` snapshot of the `todos` face.\r\n * @returns the validated list, or `undefined` when the capability is absent.\r\n */\r\nexport function readTodos(value: unknown): readonly TodoItem[] | undefined {\r\n if (value === null) return []\r\n if (!Array.isArray(value)) return undefined\r\n\r\n const items: TodoItem[] = []\r\n for (const raw of value) {\r\n if (raw === null || typeof raw !== 'object') continue\r\n const { content, status } = raw as { content?: unknown; status?: unknown }\r\n if (typeof content !== 'string') continue\r\n if (typeof status !== 'string' || !isStatus(status)) continue\r\n items.push({ content, status })\r\n }\r\n return items\r\n}\r\n\r\n/** Whether a string is one of the three lifecycle values. */\r\nfunction isStatus(value: string): value is TodoStatus {\r\n return (STATUSES as readonly string[]).includes(value)\r\n}\r\n\r\n/** Per-status counts over one list. */\r\nexport interface TodoCounts {\r\n readonly total: number\r\n readonly completed: number\r\n readonly inProgress: number\r\n readonly pending: number\r\n}\r\n\r\n/** Count a todo list by status. */\r\nexport function countTodos(todos: readonly TodoItem[]): TodoCounts {\r\n let completed = 0\r\n let inProgress = 0\r\n let pending = 0\r\n for (const item of todos) {\r\n if (item.status === 'completed') completed += 1\r\n else if (item.status === 'in_progress') inProgress += 1\r\n else pending += 1\r\n }\r\n return { total: todos.length, completed, inProgress, pending }\r\n}\r\n\r\n/** How many entries are not finished — the tab badge. */\r\nexport function unfinishedCount(todos: readonly TodoItem[]): number {\r\n const counts = countTodos(todos)\r\n return counts.inProgress + counts.pending\r\n}\r\n\r\n/**\r\n * The one-line progress summary, composed from parts so no interpolation\r\n * machinery is needed (the locale binder is a plain key → string lookup).\r\n * Mirrors the official dock's own label.\r\n */\r\nexport function progressLine(counts: TodoCounts, t: (key: string) => string): string {\r\n const parts = [`${t('status.completed')} ${counts.completed}/${counts.total}`]\r\n if (counts.inProgress > 0) parts.push(`${t('status.inProgress')} ${counts.inProgress}`)\r\n if (counts.pending > 0) parts.push(`${t('status.pending')} ${counts.pending}`)\r\n return parts.join(' · ')\r\n}\r\n","/**\r\n * Reading a live session projection from the client Session Controller.\r\n *\r\n * better-sidebar tab bodies are NOT rendered inside a DSH slot, so the\r\n * framework's `useProjection` standard prop is not available to them. The\r\n * equivalent face is reachable directly: the client Session Controller is a\r\n * cordis service on the root context, and each session binding exposes its\r\n * projections store (`SessionFace.projections.faceOf(key)`), which is the very\r\n * observable `useProjection` resolves. Since it is a bare\r\n * `{ getSnapshot, subscribe }` source, `useSyncExternalStore` binds it as-is —\r\n * no local mirror, no folding, and the value reference only changes when the\r\n * Host pushes a frame.\r\n *\r\n * Everything here is resolved defensively: a host without the Session\r\n * Controller, or a session that is not open any more, degrades to \"capability\r\n * absent\" (undefined) rather than throwing inside a render.\r\n *\r\n * This module is key-agnostic; the per-projection knowledge (which key, how to\r\n * narrow the raw value) lives with the feature modules that call it.\r\n */\r\nimport { useCallback, useMemo, useSyncExternalStore } from 'react'\r\nimport type { Context } from '@deepseek-ai/cordis'\r\n\r\n/** Structural view of a projection face (`ObservableSnapshot<unknown>`). */\r\nexport interface ProjectionFace {\r\n getSnapshot(): unknown\r\n subscribe(listener: () => void): () => void\r\n}\r\n\r\n/** Structural view of one session binding's projection store. */\r\nexport interface ProjectionsLike {\r\n faceOf(key: string): ProjectionFace\r\n}\r\n\r\n/** Structural view of the client Session Controller face this plugin uses. */\r\nexport interface SessionsLike {\r\n binding(id: string): { readonly session: { readonly projections: ProjectionsLike } } | undefined\r\n}\r\n\r\n/** A no-op unsubscribe, kept as one identity so effects never thrash. */\r\nconst NO_SUBSCRIBE = (): void => {}\r\n\r\n/** A source that never has a value — the absent-capability face. */\r\nconst ABSENT: ProjectionFace = { getSnapshot: () => undefined, subscribe: () => NO_SUBSCRIBE }\r\n\r\n/**\r\n * Whether a value really is a projection face.\r\n *\r\n * The store contract promises an identity-stable face for every key, so this\r\n * never fires against a healthy host. It exists because the fallback is worse\r\n * than the check: `useSyncExternalStore` would throw on a face-less key, and a\r\n * thrown render is a blank sidebar rather than a diagnosable \"unavailable\".\r\n */\r\nfunction isFace(value: unknown): value is ProjectionFace {\r\n if (value === null || typeof value !== 'object') return false\r\n const face = value as Partial<ProjectionFace>\r\n return typeof face.getSnapshot === 'function' && typeof face.subscribe === 'function'\r\n}\r\n\r\n/**\r\n * Resolve one projection face for one session.\r\n *\r\n * `ctx.get('sessions')` is the safe accessor (a direct `ctx.sessions` read can\r\n * throw on a context that does not carry the service yet).\r\n *\r\n * @param ctx - the client root context handed to the tab body.\r\n * @param sessionId - the tab's session scope; undefined means \"no session\".\r\n * @param key - the projection key to resolve.\r\n * @returns the projection face, or {@link ABSENT} when unreachable.\r\n */\r\nexport function resolveFace(ctx: Context | undefined, sessionId: string | undefined, key: string): ProjectionFace {\r\n if (ctx === undefined || sessionId === undefined) return ABSENT\r\n const sessions = ctx.get('sessions') as SessionsLike | undefined\r\n if (sessions === undefined || typeof sessions.binding !== 'function') return ABSENT\r\n\r\n const binding = sessions.binding(sessionId)\r\n const projections = binding?.session?.projections\r\n if (projections === undefined || typeof projections.faceOf !== 'function') return ABSENT\r\n\r\n const face: unknown = projections.faceOf(key)\r\n return isFace(face) ? face : ABSENT\r\n}\r\n\r\n/**\r\n * Read one frame of a projection without subscribing.\r\n *\r\n * The one-shot sibling of {@link useProjectionValue}, for consumers that are\r\n * not React components — the tab badge is a plain function called on every\r\n * tab-bar render, so it must neither hold a subscription nor allocate one.\r\n *\r\n * @param ctx - the client root context.\r\n * @param sessionId - the session whose projection is read.\r\n * @param key - the projection key to read.\r\n * @returns the raw snapshot: `undefined` while the capability is absent; the\r\n * semantics of any other value belong to the projection's own contract.\r\n */\r\nexport function projectionSnapshot(ctx: Context | undefined, sessionId: string | undefined, key: string): unknown {\r\n return resolveFace(ctx, sessionId, key).getSnapshot()\r\n}\r\n\r\n/**\r\n * Subscribe to one session projection by key.\r\n *\r\n * @param ctx - the client root context.\r\n * @param sessionId - the session whose projection is shown.\r\n * @param key - the projection key to subscribe to.\r\n * @returns the raw snapshot value, identity-stable across frames the host did\r\n * not change. Narrowing is the caller's job.\r\n */\r\nexport function useProjectionValue(ctx: Context | undefined, sessionId: string | undefined, key: string): unknown {\r\n const face = useMemo(() => resolveFace(ctx, sessionId, key), [ctx, sessionId, key])\r\n\r\n const subscribe = useCallback((listener: () => void) => face.subscribe(listener), [face])\r\n const getSnapshot = useCallback(() => face.getSnapshot(), [face])\r\n\r\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot)\r\n}\r\n","/**\r\n * Reading the live `todos` projection — the thin, todo-specific wrapper around\r\n * the generic face resolver (`../use-projection`). The projection key is\r\n * registered by `dsh-tool-todo`; the host pushes finished whole values and the\r\n * client never folds it. Narrowing lives in `./board` (`readTodos`).\r\n */\r\nimport { useMemo } from 'react'\r\nimport type { Context } from '@deepseek-ai/cordis'\r\nimport { readTodos } from './board'\r\nimport type { TodoItem } from './board'\r\nimport { useProjectionValue } from '../use-projection'\r\n\r\n/** The projection key registered by `dsh-tool-todo`. */\r\nexport const TODOS_KEY = 'todos'\r\n\r\n/**\r\n * Subscribe to one session's todo list.\r\n *\r\n * @param ctx - the client root context.\r\n * @param sessionId - the session whose projection is shown.\r\n * @returns the list, `[]` for a present-but-empty projection, or `undefined`\r\n * while the capability is absent.\r\n */\r\nexport function useTodos(ctx: Context | undefined, sessionId: string | undefined): readonly TodoItem[] | undefined {\r\n const raw = useProjectionValue(ctx, sessionId, TODOS_KEY)\r\n return useMemo(() => readTodos(raw), [raw])\r\n}\r\n","/**\r\n * The progress section: the `todos` projection drawn as one part of the\r\n * summary tab.\r\n *\r\n * The official composer-band panel is shadowed by this plugin (see\r\n * `brief/dock-shadow.tsx`), so the projection is read here and rendered as an\r\n * ordinary React view — no Canvas, no cross-package dependency. The full-height\r\n * shell and the scroll container live on the summary tab (`../SummaryTab`);\r\n * this component renders only its own slice and is the one section whose\r\n * absent state must stay VISIBLE: the official dock is hidden while this\r\n * plugin is mounted, so \"unavailable\" is a statement the reader needs.\r\n *\r\n * Colours come from `--dsw-alias-*` tokens only (skin contract), and the status\r\n * palette uses tokens that actually exist in the shipping theme:\r\n * `state-success-primary` / `state-business-primary` / `label-tertiary`.\r\n */\r\nimport { createElement } from 'react'\r\nimport type { CSSProperties, ReactNode } from 'react'\r\nimport type { Context } from '@deepseek-ai/cordis'\r\nimport { countTodos, progressLine, statusKey } from './brief/board'\r\nimport type { TodoItem, TodoStatus } from './brief/board'\r\nimport { useTodos } from './brief/use-todos'\r\n\r\n/** The session scope better-sidebar hands a tab body. */\r\nexport interface TodoScope {\r\n readonly sessionId?: string\r\n}\r\n\r\nexport interface TodoSectionProps {\r\n /** The DSH locale lookup, passed down from `apply`. */\r\n t: (key: string) => string\r\n /** The client root context (the tab body's only way to reach services). */\r\n ctx?: Context\r\n /** The session this tab is scoped to. */\r\n scope?: TodoScope\r\n}\r\n\r\n/** Status → token colour for the pill's text and border. */\r\nconst STATUS_COLOR: Record<TodoStatus, string> = {\r\n completed: 'var(--dsw-alias-state-success-primary)',\r\n in_progress: 'var(--dsw-alias-state-business-primary)',\r\n pending: 'var(--dsw-alias-label-tertiary)',\r\n}\r\n\r\nconst SECTION_STYLE: CSSProperties = {\r\n flexShrink: 0,\r\n borderBottom: '1px solid var(--dsw-alias-border-secondary)',\r\n}\r\n\r\nconst HEADER_STYLE: CSSProperties = {\r\n padding: '10px 12px 0',\r\n color: 'var(--dsw-alias-label-secondary)',\r\n fontSize: 12,\r\n fontWeight: 600,\r\n}\r\n\r\nconst PROGRESS_STYLE: CSSProperties = {\r\n padding: '6px 12px 10px',\r\n color: 'var(--dsw-alias-label-secondary)',\r\n fontSize: 12,\r\n}\r\n\r\nconst LIST_STYLE: CSSProperties = {\r\n listStyle: 'none',\r\n margin: 0,\r\n padding: '6px 0',\r\n display: 'flex',\r\n flexDirection: 'column',\r\n}\r\n\r\nconst ROW_STYLE: CSSProperties = {\r\n display: 'flex',\r\n alignItems: 'flex-start',\r\n gap: 8,\r\n padding: '6px 12px',\r\n}\r\n\r\n/** Pill style, coloured per status. */\r\nfunction pillStyle(status: TodoStatus): CSSProperties {\r\n return {\r\n flexShrink: 0,\r\n color: STATUS_COLOR[status],\r\n border: `1px solid ${STATUS_COLOR[status]}`,\r\n borderRadius: 999,\r\n padding: '0 6px',\r\n fontSize: 11,\r\n lineHeight: '16px',\r\n whiteSpace: 'nowrap',\r\n }\r\n}\r\n\r\n/** Task text: finished entries read as done, the working one as active. */\r\nfunction contentStyle(status: TodoStatus): CSSProperties {\r\n return {\r\n minWidth: 0,\r\n overflowWrap: 'anywhere',\r\n color:\r\n status === 'completed'\r\n ? 'var(--dsw-alias-label-tertiary)'\r\n : status === 'in_progress'\r\n ? 'var(--dsw-alias-label-primary)'\r\n : 'var(--dsw-alias-label-secondary)',\r\n textDecoration: status === 'completed' ? 'line-through' : 'none',\r\n }\r\n}\r\n\r\n/** Centred message block used by every non-list state. */\r\nexport function Notice(props: { title: string; detail?: string }): ReactNode {\r\n return (\r\n <div\r\n style={{\r\n margin: '0 auto',\r\n padding: 24,\r\n maxWidth: 460,\r\n textAlign: 'center',\r\n color: 'var(--dsw-alias-label-tertiary)',\r\n lineHeight: 1.7,\r\n fontSize: 12,\r\n }}\r\n >\r\n <div style={{ marginBottom: 6, color: 'var(--dsw-alias-label-secondary)' }}>{props.title}</div>\r\n {props.detail === undefined ? null : <div>{props.detail}</div>}\r\n </div>\r\n )\r\n}\r\n\r\n/** One task row. */\r\nfunction TodoRow(props: { item: TodoItem; t: (key: string) => string }): ReactNode {\r\n const { item, t } = props\r\n return createElement(\r\n 'li',\r\n { style: ROW_STYLE },\r\n createElement('span', { style: pillStyle(item.status) }, t(statusKey(item.status))),\r\n createElement('span', { style: contentStyle(item.status) }, item.content),\r\n )\r\n}\r\n\r\n/**\r\n * The progress section. The subscription is unconditional (a plain listener,\r\n * not IO); the three-state contract of the projection is preserved verbatim.\r\n * @param props - translation, client context, session scope.\r\n */\r\nexport function TodoSection(props: TodoSectionProps): ReactNode {\r\n const { t, ctx, scope } = props\r\n\r\n const todos = useTodos(ctx, scope?.sessionId)\r\n\r\n if (todos === undefined) {\r\n // `undefined` is the store's \"capability absent\": no session, or the host's\r\n // todo unit is unmounted. Say so rather than showing an empty board.\r\n return createElement(\r\n 'div',\r\n { style: SECTION_STYLE, 'data-dsh-brief-sidebar': 'progress-section' },\r\n createElement('div', { style: HEADER_STYLE }, t('section.progress')),\r\n createElement(Notice, { title: t('board.unavailable'), detail: t('board.unavailableHint') }),\r\n )\r\n }\r\n\r\n if (todos.length === 0) {\r\n return createElement(\r\n 'div',\r\n { style: SECTION_STYLE, 'data-dsh-brief-sidebar': 'progress-section' },\r\n createElement('div', { style: HEADER_STYLE }, t('section.progress')),\r\n createElement(Notice, { title: t('board.empty'), detail: t('board.emptyHint') }),\r\n )\r\n }\r\n\r\n const counts = countTodos(todos)\r\n\r\n return createElement(\r\n 'div',\r\n { style: SECTION_STYLE, 'data-dsh-brief-sidebar': 'progress-section' },\r\n createElement('div', { style: HEADER_STYLE }, t('section.progress')),\r\n createElement('div', { style: PROGRESS_STYLE }, progressLine(counts, t)),\r\n createElement(\r\n 'ul',\r\n { style: LIST_STYLE },\r\n todos.map((item, index) => createElement(TodoRow, { key: `${index}:${item.content}`, item, t })),\r\n ),\r\n )\r\n}\r\n\r\n/**\r\n * Backwards-compatible alias for the pre-summary export name. The component\r\n * no longer owns the full-height shell — that moved to `SummaryTab` — so the\r\n * alias exists only to keep import sites honest during the transition.\r\n */\r\nexport const TodoBoardTab = TodoSection\r\n","/**\r\n * The projection key this plugin contributes to the session-projection\r\n * registry, and its wire view.\r\n *\r\n * The key is namespaced with the plugin name on purpose: the registry refuses\r\n * to share a key across differing `stateVersion`s, so a collision with a\r\n * future host-side deliverables unit would be a hard load error, not a silent\r\n * shadow. The view type is declared structurally (the plugin has no compile\r\n * dependency on `@deepseek-ai/dsh-session-projection`); the registry accepts\r\n * any string key at runtime.\r\n */\r\n\r\n/** The projection key registered by this plugin's host half. */\r\nexport const DELIVERABLES_KEY = 'dshSummaryDeliverables'\r\n\r\n/** Client view of the produced-file fold (JSON-safe). */\r\nexport interface DeliverablesView {\r\n /** Paths produced in the latest turn, first-seen order (capped). */\r\n readonly latest: {\r\n readonly turn: number\r\n readonly paths: readonly string[]\r\n } | null\r\n /** Distinct produced paths session-wide, most-recent-first (capped). */\r\n readonly sessionPaths: readonly string[]\r\n /** Distinct produced paths session-wide, uncapped — the truthful total. */\r\n readonly sessionTotal: number\r\n}\r\n","/**\r\n * Opening a produced file's preview in the right Sidebar — the same channel\r\n * the conversation flow uses for its produced-file chips and inline mentions\r\n * (ui-chat `openFile` → `ctx.sidebarRight.openResource` with a\r\n * `dsh-resource://file/…` address).\r\n *\r\n * The address grammar is ported verbatim from the harness's browser-safe\r\n * `@deepseek-ai/dsh-util-workspace-path` (`file-address.ts` / `index.ts`),\r\n * because the plugin does not compile against that package: a relative path,\r\n * or an absolute path inside the session's workspace, is addressed under the\r\n * session's scope (`dsh-resource://file/session/<id>/<path>`); an absolute\r\n * path elsewhere keeps its absolute spelling in the same session's address.\r\n * Which tab type claims the address is the Sidebar's decision, not ours.\r\n *\r\n * The `sidebarRight` service is consumed structurally and treated as OPTIONAL:\r\n * where it is absent the section renders plain rows instead of links, the same\r\n * graceful degradation as the deliverables projection itself.\r\n */\r\nimport type { Context } from '@deepseek-ai/cordis'\r\n\r\n/** Structural view of the right-Sidebar service this plugin needs. */\r\nexport interface SidebarRightLike {\r\n openResource(url: string, options?: { params?: { line?: number } }): void\r\n}\r\n\r\n/** Structural view of the sessions service slice the opener reads. */\r\ninterface SessionsListLike {\r\n list?: { getSnapshot(): { byId?: Record<string, { cwd?: string }> } }\r\n}\r\n\r\n/** The address scheme every file resource opens with. */\r\nconst FILE_ADDRESS_PREFIX = 'dsh-resource://file/'\r\n\r\n/** Whether a path uses a Windows drive or UNC prefix. */\r\nfunction isWindowsStylePath(value: string): boolean {\r\n return /^[A-Za-z]:[/\\\\]/.test(value) || value.startsWith('\\\\\\\\')\r\n}\r\n\r\n/**\r\n * Whether a path is absolute in either spelling the Host accepts: POSIX (`/a/b`)\r\n * or Windows drive or UNC.\r\n */\r\nexport function isAbsoluteWorkspacePath(path: string): boolean {\r\n return path.startsWith('/') || isWindowsStylePath(path)\r\n}\r\n\r\n/** Component-encode one id or path segment, keeping `:` literal for drive letters. */\r\nfunction encodeSegment(segment: string): string {\r\n return encodeURIComponent(segment).replace(/%3A/gi, ':')\r\n}\r\n\r\n/** Encode a `/`-separated path segment by segment. */\r\nfunction encodePath(path: string): string {\r\n return path.split('/').map(encodeSegment).join('/')\r\n}\r\n\r\n/**\r\n * Build the address of a file read through one Session.\r\n * @param sessionId - the Session whose Host workspace resolves the path.\r\n * @param path - absolute or workspace-relative path; backslashes are normalized\r\n * to `/`, and leading `./` prefixes are dropped.\r\n */\r\nfunction sessionFileAddress(sessionId: string, path: string): string {\r\n const normalized = path.replace(/\\\\/g, '/').replace(/^(?:\\.\\/)+/, '')\r\n return `${FILE_ADDRESS_PREFIX}session/${encodeSegment(sessionId)}/${encodePath(normalized)}`\r\n}\r\n\r\n/**\r\n * The address for a path as a caller holds it.\r\n * @param sessionId - the Session the path is read in.\r\n * @param cwd - that Session's workspace root, when known.\r\n * @param path - absolute or workspace-relative path, in either separator spelling.\r\n */\r\nexport function fileAddressFor(sessionId: string, cwd: string | undefined, path: string): string {\r\n const normalized = path.replace(/\\\\/g, '/')\r\n if (!isAbsoluteWorkspacePath(normalized)) return sessionFileAddress(sessionId, normalized)\r\n const root = cwd === undefined ? '' : cwd.replace(/\\\\/g, '/').replace(/\\/+$/, '')\r\n if (root !== '' && normalized === root) return sessionFileAddress(sessionId, '')\r\n if (root !== '' && normalized.startsWith(`${root}/`)) {\r\n return sessionFileAddress(sessionId, normalized.slice(root.length + 1))\r\n }\r\n return sessionFileAddress(sessionId, normalized)\r\n}\r\n\r\n/**\r\n * Build a sidebar file opener for one session, or `undefined` when the\r\n * environment cannot open previews (no `sidebarRight` service, no session).\r\n * The cwd is re-read per call inside the opener, so a workspace switch between\r\n * renders is picked up on the next click.\r\n *\r\n * @param ctx - the client root context.\r\n * @param sessionId - the session whose workspace resolves paths.\r\n * @returns an opener that routes a produced path to the Sidebar preview.\r\n */\r\nexport function sidebarFileOpener(\r\n ctx: Context | undefined,\r\n sessionId: string | undefined,\r\n): ((path: string) => void) | undefined {\r\n if (ctx === undefined || sessionId === undefined) return undefined\r\n const bar = ctx.get('sidebarRight') as SidebarRightLike | undefined\r\n if (bar === undefined || typeof bar.openResource !== 'function') return undefined\r\n\r\n return (path: string) => {\r\n const sessions = ctx.get('sessions') as SessionsListLike | undefined\r\n const cwd = sessions?.list?.getSnapshot?.().byId?.[sessionId]?.cwd\r\n bar.openResource(fileAddressFor(sessionId, cwd, path))\r\n }\r\n}\r\n","/**\r\n * The `dshSummaryDeliverables` projection — reading and classifying it.\r\n *\r\n * The Host computes this projection (this plugin's own node half registers the\r\n * unit) and pushes finished whole values; the client never folds it. This\r\n * module is the read side of that contract and is deliberately PURE — no\r\n * React, no context, no IO — so the narrowing and the codeplan classification\r\n * can be tested directly.\r\n */\r\nimport type { DeliverablesView } from '../../projection/keys'\r\n\r\n/**\r\n * Narrow a raw projection value into a deliverables view.\r\n *\r\n * Two outcomes are kept apart on purpose, mirroring the store's contract:\r\n * - `undefined` — the capability is ABSENT (the host half never registered —\r\n * e.g. an older composition — or no frame carried the key yet);\r\n * - a view — the projection is live. A view with `latest: null` and\r\n * `sessionTotal: 0` IS the empty state; the fold starts publishing as soon\r\n * as the unit registers, so \"present but empty\" is a real value here, not a\r\n * sentinel.\r\n *\r\n * @param value - the raw `unknown` snapshot of the deliverables face.\r\n * @returns the validated view, or `undefined` when the capability is absent.\r\n */\r\nexport function readDeliverables(value: unknown): DeliverablesView | undefined {\r\n if (value === null || typeof value !== 'object' || Array.isArray(value)) return undefined\r\n const raw = value as { latest?: unknown; sessionPaths?: unknown; sessionTotal?: unknown }\r\n if (raw.latest === undefined || raw.sessionPaths === undefined || raw.sessionTotal === undefined) {\r\n return undefined\r\n }\r\n if (!isBucket(raw.latest) || !Array.isArray(raw.sessionPaths) || typeof raw.sessionTotal !== 'number') {\r\n return undefined\r\n }\r\n const paths = raw.sessionPaths.filter((path): path is string => typeof path === 'string')\r\n return {\r\n latest: raw.latest === null ? null : { turn: raw.latest.turn, paths: raw.latest.paths },\r\n sessionPaths: paths,\r\n sessionTotal: raw.sessionTotal,\r\n }\r\n}\r\n\r\n/** Whether a value is a well-formed latest-turn bucket (or explicitly null). */\r\nfunction isBucket(value: unknown): value is { turn: number; paths: readonly string[] } | null {\r\n if (value === null) return true\r\n if (typeof value !== 'object') return false\r\n const bucket = value as { turn?: unknown; paths?: unknown }\r\n return typeof bucket.turn === 'number' && Array.isArray(bucket.paths)\r\n}\r\n\r\n/**\r\n * The path prefix that identifies a codeplan artifact. The codeplan skill\r\n * writes its four fixed artifacts under `$workspace\\.agents\\plans\\<任务名>\\`;\r\n * produced paths may spell the separators either way.\r\n */\r\nconst CODEPLAN_SEGMENT = '.agents/plans/'\r\n\r\n/**\r\n * Normalize path separators so one spelling of the codeplan prefix matches.\r\n * Only classification uses this; the displayed path keeps the tool's exact\r\n * spelling.\r\n */\r\nfunction normalized(path: string): string {\r\n return path.replaceAll('\\\\', '/')\r\n}\r\n\r\n/** Whether a produced path is a codeplan artifact. */\r\nexport function isCodeplanPath(path: string): boolean {\r\n return normalized(path).includes(CODEPLAN_SEGMENT)\r\n}\r\n\r\n/**\r\n * Split a codeplan path into its task name and file name.\r\n *\r\n * @param path - a produced path; need not actually be a codeplan artifact.\r\n * @returns task and file names, or `null` when the path is not under the\r\n * codeplan plans directory (or names no file beneath the task folder).\r\n */\r\nexport function splitCodeplanPath(path: string): { task: string; file: string } | null {\r\n const marker = normalized(path).indexOf(CODEPLAN_SEGMENT)\r\n if (marker < 0) return null\r\n const rest = normalized(path).slice(marker + CODEPLAN_SEGMENT.length)\r\n const slash = rest.indexOf('/')\r\n if (slash <= 0 || slash === rest.length - 1) return null\r\n return { task: rest.slice(0, slash), file: rest.slice(slash + 1) }\r\n}\r\n\r\n/** The final path segment, under either separator. */\r\nexport function basename(path: string): string {\r\n const normalizedPath = normalized(path)\r\n const slash = normalizedPath.lastIndexOf('/')\r\n return slash < 0 ? normalizedPath : normalizedPath.slice(slash + 1)\r\n}\r\n\r\n/**\r\n * 历史文件 = 全会话去重路径 − 本回合活跃路径。\r\n *\r\n * 分隔符归一化后比较(`a\\b` 与 `a/b` 视为同一路径),保持 sessionPaths 自带的\r\n * \"最近在前\"顺序;活跃路径一个都不出现——分层契约:活跃的已在上方列表里,\r\n * 折叠区只收\"更早改过的文件\"。\r\n */\r\nexport function historyPaths(sessionPaths: readonly string[], latestPaths: readonly string[]): string[] {\r\n const active = new Set(latestPaths.map(normalized))\r\n return sessionPaths.filter((path) => !active.has(normalized(path)))\r\n}\r\n","/**\r\n * Plugin-owned i18n dictionaries.\r\n *\r\n * Consumer plugins must NOT reach into better-sidebar's internal `t()` or its\r\n * `betterSidebar` dictionary namespace, so this plugin registers its own\r\n * namespace through the DSH `locale` service. `dsh-brief-sidebar` is outside the\r\n * `LocaleNamespaceMap` merge table, which is exactly what the untyped\r\n * `register(ns, dicts)` overload is for — but the dictionaries themselves stay\r\n * strictly paired: `zh` is the key-set source of truth and every other language\r\n * (`en`, plus the fr/de/it/ru/es pack) is typed as\r\n * `Record<keyof typeof zh, string>`, so a missing or extra key is a COMPILE\r\n * error rather than a runtime fallback to the raw key.\r\n *\r\n * Every key referenced anywhere in `src/**` must exist in both dictionaries;\r\n * `tests/purity.spec.ts` asserts that, so a typo in a `t('…')` call cannot\r\n * ship as an untranslated literal.\r\n */\r\n\r\n/** The namespace owned by this plugin (own vocabulary, own lifecycle). */\r\nexport const NS = 'dsh-brief-sidebar'\r\n\r\n/** Simplified Chinese dictionary — the key-set source of truth. */\r\nexport const zh = {\r\n 'tab.title': '概要',\r\n 'tab.desc': '当前会话的任务、产物与规划文件(只读)',\r\n 'section.progress': '进展',\r\n 'section.deliverables': '产物',\r\n 'board.empty': '当前没有任务',\r\n 'board.emptyHint': '模型调用 todo_write 之后,任务会出现在这里。',\r\n 'board.unavailable': '任务暂不可用',\r\n 'board.unavailableHint': '这个会话还没有收到 todo 数据;宿主推来第一帧后会自动显示。',\r\n 'status.pending': '待处理',\r\n 'status.inProgress': '进行中',\r\n 'status.completed': '已完成',\r\n 'deliverables.empty': '本回合暂无改动文件',\r\n 'deliverables.emptyHint': '会话里成功写入或修改文件后,它们会实时出现在这里。',\r\n 'deliverables.history': '更早改动的文件 · {count}',\r\n 'deliverable.codeplanTag': '规划',\r\n 'section.memorySlots': '记忆',\r\n 'slots.empty': '当前没有激活的记忆槽位',\r\n 'slots.emptyHint': '当 dsh-prime-memory 激活记忆槽位后,它们会实时出现在这里。',\r\n 'slots.openCount': '开启 {count} 个槽位',\r\n 'slots.status.open': '生效中',\r\n 'slots.status.done': '已完成',\r\n 'slots.status.dropped': '已弃置',\r\n 'slots.status.expired': '已过期',\r\n 'settings.title': '概要侧栏',\r\n 'settings.desc': '右侧栏概要 tab 的分区开关(只读展示)。',\r\n 'settings.memorySlots': '记忆槽位分区',\r\n 'settings.memorySlotsHint': '开启后,概要 tab 在进展与产物之下显示 dsh-prime-memory 的激活记忆槽位(只读)。',\r\n 'settings.unavailable': '配置面不可用,记忆分区按默认「开」处理。',\r\n 'settings.readonly': '当前配置只读,无法在此修改。',\r\n}\r\n\r\n/** The key union: what a `t('…')` call may name inside this namespace. */\r\nexport type BriefKey = keyof typeof zh\r\n\r\n/** English dictionary, checked complete against the zh key set. */\r\nexport const en: Record<BriefKey, string> = {\r\n 'tab.title': 'Summary',\r\n 'tab.desc': 'Tasks, produced files and plan artifacts of this session (read-only)',\r\n 'section.progress': 'Progress',\r\n 'section.deliverables': 'Produced files',\r\n 'board.empty': 'No tasks yet',\r\n 'board.emptyHint': 'Tasks show up here once the model calls todo_write.',\r\n 'board.unavailable': 'Tasks unavailable',\r\n 'board.unavailableHint':\r\n 'This session has not received any todo data yet; the board fills in on the first frame.',\r\n 'status.pending': 'Pending',\r\n 'status.inProgress': 'In progress',\r\n 'status.completed': 'Done',\r\n 'deliverables.empty': 'No files changed in this turn',\r\n 'deliverables.emptyHint': 'Files written or edited successfully in the session appear here live.',\r\n 'deliverables.history': 'Earlier changes · {count}',\r\n 'deliverable.codeplanTag': 'Plan',\r\n 'section.memorySlots': 'Memory',\r\n 'slots.empty': 'No active memory slots',\r\n 'slots.emptyHint': 'Slots appear here live once dsh-prime-memory activates them.',\r\n 'slots.openCount': '{count} slots open',\r\n 'slots.status.open': 'Open',\r\n 'slots.status.done': 'Done',\r\n 'slots.status.dropped': 'Dropped',\r\n 'slots.status.expired': 'Expired',\r\n 'settings.title': 'Brief sidebar',\r\n 'settings.desc': 'Section switches for the summary tab in the right sidebar (read-only views).',\r\n 'settings.memorySlots': 'Memory slots section',\r\n 'settings.memorySlotsHint': 'When on, the summary tab lists the active memory slots from dsh-prime-memory beneath progress and produced files (read-only).',\r\n 'settings.unavailable': 'The config face is unavailable; the memory section falls back to its default (on).',\r\n 'settings.readonly': 'This config is read-only here; edits are not available.',\r\n}\r\n\r\n/** Japanese dictionary, checked complete against the zh key set. */\r\nexport const ja: Record<BriefKey, string> = {\r\n 'tab.title': '概要',\r\n 'tab.desc': 'このセッションのタスク・成果物・計画ファイル(読み取り専用)',\r\n 'section.progress': '進行状況',\r\n 'section.deliverables': '成果物',\r\n 'board.empty': '現在タスクはありません',\r\n 'board.emptyHint': 'モデルが todo_write を呼び出すと、タスクがここに表示されます。',\r\n 'board.unavailable': 'タスクを利用できません',\r\n 'board.unavailableHint':\r\n 'このセッションはまだ todo データを受信していません。ホストが最初のフレームを送ると自動的に表示されます。',\r\n 'status.pending': '未対応',\r\n 'status.inProgress': '進行中',\r\n 'status.completed': '完了',\r\n 'deliverables.empty': 'このターンで変更されたファイルはありません',\r\n 'deliverables.emptyHint': 'セッションでファイルの書き込み・変更が成功すると、ここにリアルタイムで表示されます。',\r\n 'deliverables.history': '以前の変更 · {count}',\r\n 'deliverable.codeplanTag': '計画',\r\n 'section.memorySlots': 'メモリ',\r\n 'slots.empty': 'アクティブなメモリスロットはありません',\r\n 'slots.emptyHint': 'dsh-prime-memory がスロットをアクティブにすると、ここにリアルタイムで表示されます。',\r\n 'slots.openCount': '有効なスロット: {count} 件',\r\n 'slots.status.open': '有効',\r\n 'slots.status.done': '完了',\r\n 'slots.status.dropped': '破棄',\r\n 'slots.status.expired': '期限切れ',\r\n 'settings.title': '概要サイドバー',\r\n 'settings.desc': '右サイドバー概要タブのセクション表示切り替え(読み取り専用)。',\r\n 'settings.memorySlots': 'メモリスロットセクション',\r\n 'settings.memorySlotsHint': 'オンにすると、概要タブの進行状況と成果物の下に dsh-prime-memory のアクティブなメモリスロット(読み取り専用)が表示されます。',\r\n 'settings.unavailable': '設定画面を利用できません。メモリセクションは既定(オン)で動作します。',\r\n 'settings.readonly': 'この設定はここでは読み取り専用のため変更できません。',\r\n}\r\n\r\n/** Korean dictionary, checked complete against the zh key set. */\r\nexport const ko: Record<BriefKey, string> = {\r\n 'tab.title': '요약',\r\n 'tab.desc': '현재 세션의 작업, 산출물 및 계획 파일(읽기 전용)',\r\n 'section.progress': '진행 상황',\r\n 'section.deliverables': '산출물',\r\n 'board.empty': '현재 작업이 없습니다',\r\n 'board.emptyHint': '모델이 todo_write를 호출하면 작업이 여기에 표시됩니다.',\r\n 'board.unavailable': '작업을 사용할 수 없습니다',\r\n 'board.unavailableHint':\r\n '이 세션은 아직 todo 데이터를 받지 못했습니다. 호스트가 첫 프레임을 보내면 자동으로 표시됩니다.',\r\n 'status.pending': '대기 중',\r\n 'status.inProgress': '진행 중',\r\n 'status.completed': '완료',\r\n 'deliverables.empty': '이번 턴에 변경된 파일이 없습니다',\r\n 'deliverables.emptyHint': '세션에서 파일을 성공적으로 작성하거나 수정하면 여기에 실시간으로 표시됩니다.',\r\n 'deliverables.history': '이전 변경 사항 · {count}',\r\n 'deliverable.codeplanTag': '계획',\r\n 'section.memorySlots': '메모리',\r\n 'slots.empty': '활성화된 메모리 슬롯이 없습니다',\r\n 'slots.emptyHint': 'dsh-prime-memory가 슬롯을 활성화하면 여기에 실시간으로 표시됩니다.',\r\n 'slots.openCount': '열린 슬롯: {count}개',\r\n 'slots.status.open': '활성',\r\n 'slots.status.done': '완료',\r\n 'slots.status.dropped': '폐기',\r\n 'slots.status.expired': '만료',\r\n 'settings.title': '요약 사이드바',\r\n 'settings.desc': '오른쪽 사이드바 요약 탭의 섹션 표시 전환(읽기 전용).',\r\n 'settings.memorySlots': '메모리 슬롯 섹션',\r\n 'settings.memorySlotsHint': '켜면 요약 탭의 진행 상황과 산출물 아래에 dsh-prime-memory의 활성 메모리 슬롯(읽기 전용)이 표시됩니다.',\r\n 'settings.unavailable': '설정 화면을 사용할 수 없어 메모리 섹션은 기본값(켜짐)으로 동작합니다.',\r\n 'settings.readonly': '이 설정은 여기서 읽기 전용이므로 변경할 수 없습니다.',\r\n}\r\n\r\n/** French dictionary, checked complete against the zh key set. */\r\nexport const fr: Record<BriefKey, string> = {\r\n 'tab.title': 'Résumé',\r\n 'tab.desc': 'Tâches, fichiers produits et plans de la session (lecture seule)',\r\n 'section.progress': 'Avancement',\r\n 'section.deliverables': 'Fichiers produits',\r\n 'board.empty': 'Aucune tâche pour le moment',\r\n 'board.emptyHint': 'Les tâches apparaissent ici dès que le modèle appelle todo_write.',\r\n 'board.unavailable': 'Tâches indisponibles',\r\n 'board.unavailableHint':\r\n 'Cette session n’a pas encore reçu de données todo ; le tableau se remplit dès la première trame.',\r\n 'status.pending': 'En attente',\r\n 'status.inProgress': 'En cours',\r\n 'status.completed': 'Terminé',\r\n 'deliverables.empty': 'Aucun fichier modifié à ce tour',\r\n 'deliverables.emptyHint': 'Les fichiers écrits ou modifiés dans la session apparaissent ici en direct.',\r\n 'deliverables.history': 'Modifications antérieures · {count}',\r\n 'deliverable.codeplanTag': 'Plan',\r\n 'section.memorySlots': 'Mémoire',\r\n 'slots.empty': 'Aucun emplacement mémoire actif',\r\n 'slots.emptyHint': 'Les emplacements apparaissent ici en direct dès que dsh-prime-memory les active.',\r\n 'slots.openCount': '{count} emplacements ouverts',\r\n 'slots.status.open': 'Actif',\r\n 'slots.status.done': 'Terminé',\r\n 'slots.status.dropped': 'Abandonné',\r\n 'slots.status.expired': 'Expiré',\r\n 'settings.title': 'Barre latérale Résumé',\r\n 'settings.desc': 'Interrupteurs des sections de l’onglet Résumé de la barre latérale (lecture seule).',\r\n 'settings.memorySlots': 'Section des emplacements mémoire',\r\n 'settings.memorySlotsHint': 'Une fois activé, l’onglet Résumé liste les emplacements mémoire actifs de dsh-prime-memory sous l’avancement et les fichiers produits (lecture seule).',\r\n 'settings.unavailable': 'Le canal de configuration est indisponible ; la section mémoire revient à sa valeur par défaut (activée).',\r\n 'settings.readonly': 'Cette configuration est en lecture seule ici ; aucune modification possible.',\r\n}\r\n\r\n/** German dictionary, checked complete against the zh key set. */\r\nexport const de: Record<BriefKey, string> = {\r\n 'tab.title': 'Übersicht',\r\n 'tab.desc': 'Aufgaben, erzeugte Dateien und Pläne dieser Sitzung (schreibgeschützt)',\r\n 'section.progress': 'Fortschritt',\r\n 'section.deliverables': 'Erzeugte Dateien',\r\n 'board.empty': 'Noch keine Aufgaben',\r\n 'board.emptyHint': 'Aufgaben erscheinen hier, sobald das Modell todo_write aufruft.',\r\n 'board.unavailable': 'Aufgaben nicht verfügbar',\r\n 'board.unavailableHint':\r\n 'Diese Sitzung hat noch keine Todo-Daten erhalten; das Board füllt sich mit dem ersten Frame.',\r\n 'status.pending': 'Ausstehend',\r\n 'status.inProgress': 'In Bearbeitung',\r\n 'status.completed': 'Fertig',\r\n 'deliverables.empty': 'Keine Dateien in diesem Durchgang geändert',\r\n 'deliverables.emptyHint': 'In der Sitzung erfolgreich geschriebene oder geänderte Dateien erscheinen hier live.',\r\n 'deliverables.history': 'Frühere Änderungen · {count}',\r\n 'deliverable.codeplanTag': 'Plan',\r\n 'section.memorySlots': 'Gedächtnis',\r\n 'slots.empty': 'Keine aktiven Speicherplätze',\r\n 'slots.emptyHint': 'Sobald dsh-prime-memory Slots aktiviert, erscheinen sie hier in Echtzeit.',\r\n 'slots.openCount': '{count} Slots offen',\r\n 'slots.status.open': 'Offen',\r\n 'slots.status.done': 'Erledigt',\r\n 'slots.status.dropped': 'Verworfen',\r\n 'slots.status.expired': 'Abgelaufen',\r\n 'settings.title': 'Übersicht-Seitenleiste',\r\n 'settings.desc': 'Schalter für die Abschnitte des Übersicht-Tabs in der rechten Seitenleiste (schreibgeschützt).',\r\n 'settings.memorySlots': 'Speicherplätze-Abschnitt',\r\n 'settings.memorySlotsHint': 'Ist der Schalter eingeschaltet, listet der Übersicht-Tab die aktiven Speicherplätze aus dsh-prime-memory unter Fortschritt und erzeugten Dateien auf (schreibgeschützt).',\r\n 'settings.unavailable': 'Die Konfigurationsoberfläche ist nicht verfügbar; der Gedächtnis-Abschnitt fällt auf den Standard (ein) zurück.',\r\n 'settings.readonly': 'Diese Konfiguration ist hier schreibgeschützt; Änderungen sind nicht möglich.',\r\n}\r\n\r\n/** Italian dictionary, checked complete against the zh key set. */\r\nexport const it: Record<BriefKey, string> = {\r\n 'tab.title': 'Riepilogo',\r\n 'tab.desc': 'Attività, file prodotti e piani di questa sessione (sola lettura)',\r\n 'section.progress': 'Avanzamento',\r\n 'section.deliverables': 'File prodotti',\r\n 'board.empty': 'Nessuna attività per ora',\r\n 'board.emptyHint': 'Le attività appaiono qui quando il modello chiama todo_write.',\r\n 'board.unavailable': 'Attività non disponibili',\r\n 'board.unavailableHint':\r\n 'Questa sessione non ha ancora ricevuto dati todo; la bacheca si riempirà al primo frame.',\r\n 'status.pending': 'In attesa',\r\n 'status.inProgress': 'In corso',\r\n 'status.completed': 'Completato',\r\n 'deliverables.empty': 'Nessun file modificato in questo turno',\r\n 'deliverables.emptyHint': 'I file scritti o modificati nella sessione appaiono qui in tempo reale.',\r\n 'deliverables.history': 'Modifiche precedenti · {count}',\r\n 'deliverable.codeplanTag': 'Piano',\r\n 'section.memorySlots': 'Memoria',\r\n 'slots.empty': 'Nessuno slot di memoria attivo',\r\n 'slots.emptyHint': 'Gli slot appaiono qui in tempo reale quando dsh-prime-memory li attiva.',\r\n 'slots.openCount': '{count} slot aperti',\r\n 'slots.status.open': 'Attivo',\r\n 'slots.status.done': 'Completato',\r\n 'slots.status.dropped': 'Scartato',\r\n 'slots.status.expired': 'Scaduto',\r\n 'settings.title': 'Barra laterale Riepilogo',\r\n 'settings.desc': 'Interruttori delle sezioni della scheda Riepilogo nella barra laterale destra (sola lettura).',\r\n 'settings.memorySlots': 'Sezione slot di memoria',\r\n 'settings.memorySlotsHint': 'Quando attivo, la scheda Riepilogo elenca gli slot di memoria attivi di dsh-prime-memory sotto avanzamento e file prodotti (sola lettura).',\r\n 'settings.unavailable': 'La superficie di configurazione non è disponibile; la sezione memoria torna al valore predefinito (attivo).',\r\n 'settings.readonly': 'Questa configurazione è in sola lettura qui; non è possibile modificarla.',\r\n}\r\n\r\n/** Russian dictionary, checked complete against the zh key set. */\r\nexport const ru: Record<BriefKey, string> = {\r\n 'tab.title': 'Сводка',\r\n 'tab.desc': 'Задачи, созданные файлы и планы этой сессии (только чтение)',\r\n 'section.progress': 'Ход работы',\r\n 'section.deliverables': 'Созданные файлы',\r\n 'board.empty': 'Задач пока нет',\r\n 'board.emptyHint': 'Задачи появятся здесь, когда модель вызовет todo_write.',\r\n 'board.unavailable': 'Задачи недоступны',\r\n 'board.unavailableHint':\r\n 'Эта сессия ещё не получила данные todo; доска заполнится после первого кадра.',\r\n 'status.pending': 'Ожидает',\r\n 'status.inProgress': 'В работе',\r\n 'status.completed': 'Готово',\r\n 'deliverables.empty': 'За этот ход файлы не менялись',\r\n 'deliverables.emptyHint': 'Успешно записанные или изменённые файлы появляются здесь в реальном времени.',\r\n 'deliverables.history': 'Более ранние изменения · {count}',\r\n 'deliverable.codeplanTag': 'План',\r\n 'section.memorySlots': 'Память',\r\n 'slots.empty': 'Активных слотов памяти нет',\r\n 'slots.emptyHint': 'Как только dsh-prime-memory активирует слоты, они появляются здесь в реальном времени.',\r\n 'slots.openCount': 'Открытых слотов: {count}',\r\n 'slots.status.open': 'Активен',\r\n 'slots.status.done': 'Завершён',\r\n 'slots.status.dropped': 'Отброшен',\r\n 'slots.status.expired': 'Истёк',\r\n 'settings.title': 'Боковая панель «Сводка»',\r\n 'settings.desc': 'Переключатели разделов вкладки «Сводка» на правой панели (только чтение).',\r\n 'settings.memorySlots': 'Раздел слотов памяти',\r\n 'settings.memorySlotsHint': 'Когда включено, вкладка «Сводка» показывает активные слоты памяти из dsh-prime-memory под ходом работы и созданными файлами (только чтение).',\r\n 'settings.unavailable': 'Панель конфигурации недоступна; раздел памяти работает по умолчанию (включён).',\r\n 'settings.readonly': 'Эта конфигурация здесь только для чтения; изменить её нельзя.',\r\n}\r\n\r\n/** Spanish dictionary, checked complete against the zh key set. */\r\nexport const es: Record<BriefKey, string> = {\r\n 'tab.title': 'Resumen',\r\n 'tab.desc': 'Tareas, archivos generados y planes de esta sesión (solo lectura)',\r\n 'section.progress': 'Avance',\r\n 'section.deliverables': 'Archivos generados',\r\n 'board.empty': 'Aún no hay tareas',\r\n 'board.emptyHint': 'Las tareas aparecen aquí cuando el modelo llama a todo_write.',\r\n 'board.unavailable': 'Tareas no disponibles',\r\n 'board.unavailableHint':\r\n 'Esta sesión aún no ha recibido datos de tareas; el tablero se llena con el primer fotograma.',\r\n 'status.pending': 'Pendiente',\r\n 'status.inProgress': 'En curso',\r\n 'status.completed': 'Hecho',\r\n 'deliverables.empty': 'Ningún archivo modificado en este turno',\r\n 'deliverables.emptyHint': 'Los archivos escritos o editados en la sesión aparecen aquí en directo.',\r\n 'deliverables.history': 'Cambios anteriores · {count}',\r\n 'deliverable.codeplanTag': 'Plan',\r\n 'section.memorySlots': 'Memoria',\r\n 'slots.empty': 'No hay ranuras de memoria activas',\r\n 'slots.emptyHint': 'Las ranuras aparecen aquí en directo en cuanto dsh-prime-memory las active.',\r\n 'slots.openCount': '{count} ranuras abiertas',\r\n 'slots.status.open': 'Activa',\r\n 'slots.status.done': 'Hecha',\r\n 'slots.status.dropped': 'Descartada',\r\n 'slots.status.expired': 'Caducada',\r\n 'settings.title': 'Barra lateral Resumen',\r\n 'settings.desc': 'Interruptores de las secciones de la pestaña Resumen en la barra lateral derecha (solo lectura).',\r\n 'settings.memorySlots': 'Sección de ranuras de memoria',\r\n 'settings.memorySlotsHint': 'Al activarla, la pestaña Resumen enumera las ranuras de memoria activas de dsh-prime-memory bajo el avance y los archivos generados (solo lectura).',\r\n 'settings.unavailable': 'La superficie de configuración no está disponible; la sección de memoria vuelve a su valor por defecto (activada).',\r\n 'settings.readonly': 'Esta configuración es de solo lectura aquí; no se puede modificar.',\r\n}\r\n\r\n/**\r\n * All locales in the shape the locale service consumes. Registered in ONE call:\r\n * the registry rejects a duplicate `(namespace, locale)` pair and the\r\n * per-locale form would leave a half-registered namespace if a later call\r\n * threw. `zh`/`en` are the host built-ins; the fr/de/it/ru/es pack rides the\r\n * same map form (every id is a valid BCP 47-style tag, so the registry accepts\r\n * it as-is).\r\n */\r\nexport const dictionaries: Record<string, Record<string, string>> = {\r\n zh,\r\n en,\r\n ja,\r\n ko,\r\n fr,\r\n de,\r\n it,\r\n ru,\r\n es,\r\n}\r\n\r\n/**\r\n * Expand the one `{count}` placeholder the deliverables totals line uses. The\r\n * locale binder is a plain key → string lookup (no interpolation machinery),\r\n * mirroring how `progressLine` composes its summary from parts.\r\n */\r\nexport function expandCount(template: string, count: number): string {\r\n return template.replace('{count}', String(count))\r\n}\r\n","/**\r\n * The deliverables section: the `dshSummaryDeliverables` projection drawn as\r\n * one part of the summary tab.\r\n *\r\n * Unlike the progress section this is an OPTIONAL capability: where the host\r\n * half never registered the unit (older composition, key absent), the section\r\n * hides entirely — absence is not an error state here, and the official\r\n * \"本次产出\" row in the conversation flow still covers the turn-end view.\r\n * When the projection is live, the latest turn's produced paths update\r\n * mid-turn: every successful mutation result publishes a frame, so the reader\r\n * sees files appear while the turn is still running, which is exactly the gap\r\n * the official turn-tail row has.\r\n *\r\n * Codeplan artifacts (paths under `.agents/plans/<任务名>/`) are ordinary\r\n * produced entries — they are marked with a pill, not split into a separate\r\n * section.\r\n *\r\n * Colours come from `--dsw-alias-*` tokens only (skin contract).\r\n */\r\nimport { createElement } from 'react'\r\nimport { useState } from 'react'\r\nimport type { CSSProperties, ReactNode } from 'react'\r\nimport type { Context } from '@deepseek-ai/cordis'\r\nimport { DELIVERABLES_KEY } from '../../projection/keys'\r\nimport { useProjectionValue } from '../use-projection'\r\nimport { sidebarFileOpener } from '../file-open'\r\nimport { basename, historyPaths, readDeliverables, splitCodeplanPath } from './deliverables'\r\nimport { expandCount } from '../locales'\r\n\r\nexport interface DeliverablesSectionProps {\r\n /** The DSH locale lookup, passed down from `apply`. */\r\n t: (key: string) => string\r\n /** The client root context (the tab body's only way to reach services). */\r\n ctx?: Context\r\n /** The session this tab is scoped to. */\r\n scope?: { readonly sessionId?: string }\r\n}\r\n\r\nconst SECTION_STYLE: CSSProperties = {\r\n flexShrink: 0,\r\n borderBottom: '1px solid var(--dsw-alias-border-secondary)',\r\n}\r\n\r\nconst HEADER_STYLE: CSSProperties = {\r\n padding: '10px 12px 0',\r\n color: 'var(--dsw-alias-label-secondary)',\r\n fontSize: 12,\r\n fontWeight: 600,\r\n}\r\n\r\nconst LIST_STYLE: CSSProperties = {\r\n listStyle: 'none',\r\n margin: 0,\r\n padding: '6px 0',\r\n display: 'flex',\r\n flexDirection: 'column',\r\n}\r\n\r\nconst ROW_STYLE: CSSProperties = {\r\n display: 'flex',\r\n alignItems: 'flex-start',\r\n gap: 8,\r\n padding: '6px 12px',\r\n}\r\n\r\n/** 折叠区开关行:与文件行同宽,tertiary 强调,整行可点。 */\r\nconst HISTORY_TOGGLE_STYLE: CSSProperties = {\r\n background: 'none',\r\n border: 'none',\r\n font: 'inherit',\r\n fontSize: 12,\r\n color: 'var(--dsw-alias-label-tertiary)',\r\n cursor: 'pointer',\r\n display: 'flex',\r\n alignItems: 'center',\r\n gap: 4,\r\n padding: '6px 12px',\r\n textAlign: 'left',\r\n width: 'calc(100% - 24px)',\r\n}\r\n\r\nconst CHEVRON_STYLE = (open: boolean): CSSProperties => ({\r\n flexShrink: 0,\r\n transition: 'transform 0.16s',\r\n transform: open ? 'rotate(180deg)' : 'none',\r\n})\r\n\r\nconst PATH_STYLE: CSSProperties = {\r\n minWidth: 0,\r\n overflowWrap: 'anywhere',\r\n color: 'var(--dsw-alias-label-secondary)',\r\n}\r\n\r\n/** Row-as-button reset: the row itself is the click target, content unchanged. */\r\nconst BUTTON_STYLE: CSSProperties = {\r\n ...PATH_STYLE,\r\n background: 'none',\r\n border: 'none',\r\n font: 'inherit',\r\n fontSize: 'inherit',\r\n color: 'var(--dsw-alias-label-secondary)',\r\n padding: 0,\r\n textAlign: 'left',\r\n cursor: 'pointer',\r\n}\r\n\r\n/** The codeplan pill: same form as the status pills, tertiary emphasis. */\r\nconst TAG_STYLE: CSSProperties = {\r\n flexShrink: 0,\r\n color: 'var(--dsw-alias-label-tertiary)',\r\n border: '1px solid var(--dsw-alias-label-tertiary)',\r\n borderRadius: 999,\r\n padding: '0 6px',\r\n fontSize: 11,\r\n lineHeight: '16px',\r\n whiteSpace: 'nowrap',\r\n}\r\n\r\n/**\r\n * One produced-file row. When the sidebar opener is available the row is a\r\n * button that routes the path to the Sidebar preview (the same channel the\r\n * conversation's produced-file chips use); otherwise it degrades to a plain\r\n * span. A codeplan artifact carries the pill either way.\r\n */\r\nfunction PathRow(props: {\r\n path: string\r\n t: (key: string) => string\r\n open?: (path: string) => void\r\n}): ReactNode {\r\n const { path, t, open } = props\r\n const plan = splitCodeplanPath(path)\r\n const title = plan === null ? path : `${t('deliverable.codeplanTag')}: ${plan.task} · ${path}`\r\n const label = basename(path)\r\n const content = plan === null ? null : createElement('span', { style: TAG_STYLE }, t('deliverable.codeplanTag'))\r\n if (open === undefined) {\r\n return createElement(\r\n 'li',\r\n { style: ROW_STYLE },\r\n content,\r\n createElement('span', { style: PATH_STYLE, title }, label),\r\n )\r\n }\r\n return createElement(\r\n 'li',\r\n { style: ROW_STYLE },\r\n content,\r\n createElement('button', { style: BUTTON_STYLE, title, onClick: () => { open(path) } }, label),\r\n )\r\n}\r\n\r\n/**\r\n * The deliverables section. The subscription is unconditional (a plain\r\n * listener, not IO).\r\n *\r\n * 分层:活跃文件(本回合 latest)直接列表;全会话更早改动折叠在开关行后面\r\n * (与活跃文件按归一化路径去重),展开后是与活跃区相同的可点击文件行。\r\n * @param props - translation, client context, session scope.\r\n */\r\nexport function DeliverablesSection(props: DeliverablesSectionProps): ReactNode {\r\n const { t, ctx, scope } = props\r\n const [historyOpen, setHistoryOpen] = useState(false)\r\n\r\n const view = readDeliverables(useProjectionValue(ctx, scope?.sessionId, DELIVERABLES_KEY))\r\n\r\n // Capability absent — the optional unit never registered. Hide, don't nag.\r\n if (view === undefined) return null\r\n\r\n // Optional click-to-preview: rows degrade to plain text where the sidebar\r\n // opener is absent (the opener resolves the session cwd per click).\r\n const open = sidebarFileOpener(ctx, scope?.sessionId)\r\n\r\n const latestPaths = view.latest?.paths ?? []\r\n const history = historyPaths(view.sessionPaths, latestPaths)\r\n\r\n if (latestPaths.length === 0 && view.sessionTotal === 0) {\r\n return createElement(\r\n 'div',\r\n { style: SECTION_STYLE, 'data-dsh-brief-sidebar': 'deliverables-section' },\r\n createElement('div', { style: HEADER_STYLE }, t('section.deliverables')),\r\n createElement(\r\n 'div',\r\n {\r\n style: {\r\n margin: '0 auto',\r\n padding: 24,\r\n maxWidth: 460,\r\n textAlign: 'center',\r\n color: 'var(--dsw-alias-label-tertiary)',\r\n lineHeight: 1.7,\r\n fontSize: 12,\r\n },\r\n },\r\n createElement('div', { style: { marginBottom: 6, color: 'var(--dsw-alias-label-secondary)' } }, t('deliverables.empty')),\r\n createElement('div', null, t('deliverables.emptyHint')),\r\n ),\r\n )\r\n }\r\n\r\n return createElement(\r\n 'div',\r\n { style: SECTION_STYLE, 'data-dsh-brief-sidebar': 'deliverables-section' },\r\n createElement('div', { style: HEADER_STYLE }, t('section.deliverables')),\r\n createElement(\r\n 'ul',\r\n { style: LIST_STYLE },\r\n latestPaths.map(path => createElement(PathRow, { key: path, path, t, open })),\r\n ),\r\n history.length > 0 && createElement(\r\n 'button',\r\n {\r\n type: 'button',\r\n 'aria-expanded': historyOpen,\r\n style: HISTORY_TOGGLE_STYLE,\r\n onClick: () => { setHistoryOpen(current => !current) },\r\n },\r\n expandCount(t('deliverables.history'), history.length),\r\n createElement(\r\n 'svg',\r\n { width: 10, height: 10, viewBox: '0 0 16 16', 'aria-hidden': true, style: CHEVRON_STYLE(historyOpen) },\r\n createElement('path', {\r\n d: 'M4 6l4 4 4-4',\r\n fill: 'none',\r\n stroke: 'currentColor',\r\n strokeWidth: 1.5,\r\n strokeLinecap: 'round',\r\n strokeLinejoin: 'round',\r\n }),\r\n ),\r\n ),\r\n historyOpen && createElement(\r\n 'ul',\r\n { style: LIST_STYLE },\r\n history.map(path => createElement(PathRow, { key: path, path, t, open })),\r\n ),\r\n )\r\n}\r\n","/**\r\n * The `memorySlots` projection — reading it.\r\n *\r\n * The projection unit is registered by `dsh-prime-memory` (key `memorySlots`,\r\n * wire view `{rev, count, openCount, slots[]}`); this plugin does NOT depend\r\n * on it — when the unit is not registered the client reads `undefined` from\r\n * `faceOf` and the section hides. This module is the read side of that\r\n * contract and is deliberately PURE — no React, no context, no IO — so the\r\n * narrowing can be tested directly.\r\n *\r\n * Forward compatibility: the shipped view carries `{id, title, kind, status,\r\n * priority}` only. A future prime-memory release may add `body`/`refs` to the\r\n * wire view (its projection plan keeps that door open); those fields are\r\n * narrowed opportunistically — present and well-formed → kept, absent or\r\n * malformed → dropped — so the section renders them without a brief-sidebar\r\n * change, and a malformed extra field can never blank the section.\r\n */\r\n\r\n/** The projection key `dsh-prime-memory` registers (its canonical constant). */\r\nexport const MEMORY_SLOTS_KEY = 'memorySlots'\r\n\r\n/** One slot of the wire view. */\r\nexport interface MemorySlotView {\r\n readonly id: string\r\n readonly title: string\r\n /** One of prime-memory's `SLOT_KINDS`: rule / todo / anchor / pointer. */\r\n readonly kind: string\r\n /** One of prime-memory's `SLOT_STATUSES`: open / done / dropped / expired. */\r\n readonly status: string\r\n /** 0–100; higher wins the activation competition. */\r\n readonly priority: number\r\n /** Slot body, once prime-memory exposes it on the wire (truncated at render). */\r\n readonly body?: string\r\n /** Reference targets, once prime-memory exposes them on the wire. */\r\n readonly refs?: readonly string[]\r\n /**\r\n * record_id 引用的展示解析(v0.5.0 契约:ref → `[type] 名称简述`),由\r\n * prime-memory 在投影帧构建时解析;路径/URL 类引用不产生条目。存在才收。\r\n */\r\n readonly refViews?: readonly SlotRefView[]\r\n}\r\n\r\n/** 一条 record_id 引用的展示解析(与 prime-memory 的 SlotRefView 同形)。 */\r\nexport interface SlotRefView {\r\n readonly ref: string\r\n readonly title: string\r\n}\r\n\r\n/** The whole wire view of one frame. */\r\nexport interface MemorySlotsView {\r\n readonly rev: number\r\n readonly count: number\r\n readonly openCount: number\r\n readonly slots: readonly MemorySlotView[]\r\n}\r\n\r\n/**\r\n * Narrow a raw projection value into a memory-slots view.\r\n *\r\n * Two outcomes are kept apart, mirroring the deliverables read side:\r\n * - `undefined` — the capability is ABSENT (prime-memory is not in the\r\n * composition, or no frame has carried the key yet), or the frame is\r\n * malformed beyond repair (a shape drift must not blank the whole tab);\r\n * - a view — the projection is live. A view with `slots: []` IS the empty\r\n * state: prime-memory publishes as soon as the unit registers, so \"present\r\n * but empty\" is a real value, not a sentinel.\r\n *\r\n * Malformed slot entries are dropped rather than thrown on (trusted-Host\r\n * value, defensive display), like `readTodos`.\r\n *\r\n * @param value - the raw `unknown` snapshot of the `memorySlots` face.\r\n * @returns the validated view, or `undefined` when the capability is absent.\r\n */\r\nexport function readSlots(value: unknown): MemorySlotsView | undefined {\r\n if (value === null || typeof value !== 'object' || Array.isArray(value)) return undefined\r\n const raw = value as Record<string, unknown>\r\n if (!isCounter(raw['rev']) || !isCounter(raw['count']) || !isCounter(raw['openCount'])) return undefined\r\n if (!Array.isArray(raw['slots'])) return undefined\r\n const slots: MemorySlotView[] = []\r\n for (const entry of raw['slots']) {\r\n const slot = readSlot(entry)\r\n if (slot !== undefined) slots.push(slot)\r\n }\r\n return { rev: raw['rev'], count: raw['count'], openCount: raw['openCount'], slots }\r\n}\r\n\r\n/** Whether a value is a non-negative safe integer (the view's counter shape). */\r\nfunction isCounter(value: unknown): value is number {\r\n return typeof value === 'number' && Number.isSafeInteger(value) && value >= 0\r\n}\r\n\r\n/** Narrow one slot entry; `undefined` = malformed, dropped. */\r\nfunction readSlot(value: unknown): MemorySlotView | undefined {\r\n if (value === null || typeof value !== 'object' || Array.isArray(value)) return undefined\r\n const raw = value as Record<string, unknown>\r\n if (typeof raw['id'] !== 'string' || raw['id'].length === 0) return undefined\r\n if (typeof raw['title'] !== 'string') return undefined\r\n if (typeof raw['kind'] !== 'string' || typeof raw['status'] !== 'string') return undefined\r\n if (typeof raw['priority'] !== 'number') return undefined\r\n const slot: { -readonly [K in keyof MemorySlotView]: MemorySlotView[K] } = {\r\n id: raw['id'],\r\n title: raw['title'],\r\n kind: raw['kind'],\r\n status: raw['status'],\r\n priority: raw['priority'],\r\n }\r\n if (typeof raw['body'] === 'string' && raw['body'].length > 0) slot.body = raw['body']\r\n if (Array.isArray(raw['refs'])) {\r\n const refs = raw['refs'].filter((ref): ref is string => typeof ref === 'string' && ref.length > 0)\r\n if (refs.length > 0) slot.refs = refs\r\n }\r\n if (Array.isArray(raw['refViews'])) {\r\n const views: SlotRefView[] = []\r\n for (const entry of raw['refViews']) {\r\n if (entry === null || typeof entry !== 'object' || Array.isArray(entry)) continue\r\n const view = entry as Record<string, unknown>\r\n if (typeof view['ref'] !== 'string' || view['ref'].length === 0) continue\r\n if (typeof view['title'] !== 'string' || view['title'].length === 0) continue\r\n views.push({ ref: view['ref'], title: view['title'] })\r\n }\r\n if (views.length > 0) slot.refViews = views\r\n }\r\n return slot\r\n}\r\n\r\n/** The body length at which the section truncates (full text rides the title tooltip). */\r\nexport const BODY_TRUNCATE_AT = 120\r\n\r\n/**\r\n * One-line truncation for a slot body. Already-short bodies pass through;\r\n * longer ones are cut at the limit with an ellipsis.\r\n */\r\nexport function truncateBody(body: string, limit = BODY_TRUNCATE_AT): string {\r\n const flat = body.replaceAll(/\\s+/g, ' ').trim()\r\n return flat.length <= limit ? flat : `${flat.slice(0, limit)}…`\r\n}\r\n","/**\r\n * Structural views of the `configForms` settings seam (DSH 0.1.7+).\r\n *\r\n * The settings card and the memory-section gate consume only these members of\r\n * the `ConfigForm` handle that `configForms.get(entryId)` returns, so the real\r\n * `@deepseek-ai/dsh-client-ui-settings/client` type graph is never imported —\r\n * the same reason `use-projection.ts` declares its own session faces and\r\n * `dock-shadow.tsx` its own slots face.\r\n *\r\n * `configForms` serves the STORED doc: a `.volatile()` field arrives as a\r\n * plain value here (the host unwraps the live refs before persistence), so\r\n * the client reads a plain config shape.\r\n */\r\n\r\n/** One snapshot of an entry's stored config. */\r\nexport interface SettingsScopeSnapshot<T> {\r\n status: 'loading' | 'ready' | 'unavailable'\r\n value: T | undefined\r\n revision: number | undefined\r\n /** Whether the current face may write (a read-only seat disables the controls). */\r\n writable: boolean\r\n}\r\n\r\n/** The `ConfigForm` handle face this plugin uses. */\r\nexport interface SettingsScope<T> {\r\n getSnapshot(): SettingsScopeSnapshot<T>\r\n subscribe(listener: () => void): () => void\r\n set(field: string, value: unknown): Promise<boolean>\r\n unset(field: string): Promise<boolean>\r\n}\r\n\r\n/** Structural view of the `configForms` cordis service, keyed by entry id. */\r\nexport interface ConfigFormsLike {\r\n get<T>(entryId: string): SettingsScope<T>\r\n}\r\n\r\n/**\r\n * This plugin's entry config, as the client reads it. Only the fields the\r\n * browser half consumes are declared; the schema (node half `Config`) owns\r\n * the full surface.\r\n */\r\nexport interface BriefClientConfig {\r\n /** Whether the summary tab renders the memory-slots section (default: on). */\r\n showMemorySlots?: boolean\r\n}\r\n\r\n/**\r\n * The switch's default. Applies wherever the config face cannot answer:\r\n * `configForms` absent, the scope not `ready`, or the field unset. The\r\n * section still only appears when the `memorySlots` projection exists, so\r\n * \"default on\" cannot paint anything on a host without prime-memory.\r\n */\r\nexport const DEFAULT_SHOW_MEMORY_SLOTS = true\r\n\r\n/**\r\n * Resolve the switch from one snapshot (pure; testable). Any\r\n * cannot-answer shape falls back to {@link DEFAULT_SHOW_MEMORY_SLOTS}; an\r\n * explicit `false` is the only off.\r\n */\r\nexport function resolveShowMemorySlots(\r\n snapshot: SettingsScopeSnapshot<BriefClientConfig> | undefined,\r\n): boolean {\r\n if (snapshot === undefined || snapshot.status !== 'ready') return DEFAULT_SHOW_MEMORY_SLOTS\r\n return snapshot.value?.showMemorySlots !== false\r\n}\r\n","/**\r\n * The memory-slots section: the `memorySlots` projection drawn as the third\r\n * part of the summary tab (fixed last: 进展 → 产物 → 记忆).\r\n *\r\n * TWO gates, both fail-open to \"hidden\", so a host without dsh-prime-memory\r\n * paints nothing here:\r\n *\r\n * 1. **The switch.** This plugin's entry config (`configForms` → entry\r\n * `dsh-brief-sidebar`, field `showMemorySlots`, default on). When the\r\n * config face cannot answer (service absent, scope not ready, field\r\n * unset) the default applies — see `scope-face.ts`.\r\n * 2. **The capability.** The `memorySlots` projection must exist for the\r\n * session; `undefined` hides the section entirely, exactly like the\r\n * optional deliverables section.\r\n *\r\n * Colours come from `--dsw-alias-*` tokens only (skin contract, enforced by\r\n * `tests/purity.spec.ts`).\r\n */\r\nimport { useCallback, useMemo, useSyncExternalStore } from 'react'\r\nimport { createElement } from 'react'\r\nimport type { CSSProperties, ReactNode } from 'react'\r\nimport type { Context } from '@deepseek-ai/cordis'\r\nimport {\r\n MEMORY_SLOTS_KEY,\r\n readSlots,\r\n truncateBody,\r\n} from './memory-slots'\r\nimport type { MemorySlotView, MemorySlotsView } from './memory-slots'\r\nimport { resolveShowMemorySlots } from '../scope-face'\r\nimport type { BriefClientConfig, ConfigFormsLike, SettingsScope, SettingsScopeSnapshot } from '../scope-face'\r\nimport { useProjectionValue } from '../use-projection'\r\nimport { expandCount } from '../locales'\r\n\r\nexport interface MemorySlotsSectionProps {\r\n /** The DSH locale lookup, passed down from `apply`. */\r\n t: (key: string) => string\r\n /** The client root context (the tab body's only way to reach services). */\r\n ctx?: Context\r\n /** The session this tab is scoped to. */\r\n scope?: { readonly sessionId?: string }\r\n}\r\n\r\nconst SECTION_STYLE: CSSProperties = {\r\n flexShrink: 0,\r\n borderBottom: '1px solid var(--dsw-alias-border-secondary)',\r\n}\r\n\r\nconst HEADER_STYLE: CSSProperties = {\r\n padding: '10px 12px 0',\r\n color: 'var(--dsw-alias-label-secondary)',\r\n fontSize: 12,\r\n fontWeight: 600,\r\n}\r\n\r\nconst LIST_STYLE: CSSProperties = {\r\n listStyle: 'none',\r\n margin: 0,\r\n padding: '6px 0',\r\n display: 'flex',\r\n flexDirection: 'column',\r\n}\r\n\r\nconst ROW_STYLE: CSSProperties = {\r\n display: 'flex',\r\n alignItems: 'flex-start',\r\n gap: 8,\r\n padding: '6px 12px',\r\n}\r\n\r\nconst TITLE_STYLE: CSSProperties = {\r\n minWidth: 0,\r\n overflowWrap: 'anywhere',\r\n color: 'var(--dsw-alias-label-secondary)',\r\n}\r\n\r\nconst META_STYLE: CSSProperties = {\r\n flexShrink: 0,\r\n color: 'var(--dsw-alias-label-tertiary)',\r\n fontSize: 11,\r\n lineHeight: '18px',\r\n whiteSpace: 'nowrap',\r\n}\r\n\r\n/** The kind pill: same form as the codeplan pill, tertiary emphasis. */\r\nconst KIND_STYLE: CSSProperties = {\r\n flexShrink: 0,\r\n color: 'var(--dsw-alias-label-tertiary)',\r\n border: '1px solid var(--dsw-alias-label-tertiary)',\r\n borderRadius: 999,\r\n padding: '0 6px',\r\n fontSize: 11,\r\n lineHeight: '16px',\r\n whiteSpace: 'nowrap',\r\n}\r\n\r\n/** The status pill: filled tint for an open slot, plain text otherwise. */\r\nconst STATUS_OPEN_STYLE: CSSProperties = {\r\n flexShrink: 0,\r\n color: 'var(--dsw-alias-label-primary)',\r\n border: '1px solid var(--dsw-alias-label-secondary)',\r\n borderRadius: 999,\r\n padding: '0 6px',\r\n fontSize: 11,\r\n lineHeight: '16px',\r\n whiteSpace: 'nowrap',\r\n}\r\n\r\nconst STATUS_SETTLED_STYLE: CSSProperties = {\r\n ...STATUS_OPEN_STYLE,\r\n color: 'var(--dsw-alias-label-tertiary)',\r\n border: '1px solid var(--dsw-alias-label-tertiary)',\r\n}\r\n\r\nconst BODY_STYLE: CSSProperties = {\r\n margin: 0,\r\n minWidth: 0,\r\n overflowWrap: 'anywhere',\r\n color: 'var(--dsw-alias-label-tertiary)',\r\n fontSize: 12,\r\n lineHeight: '18px',\r\n}\r\n\r\nconst REFS_STYLE: CSSProperties = {\r\n ...BODY_STYLE,\r\n fontSize: 11,\r\n}\r\n\r\nconst TOTAL_STYLE: CSSProperties = {\r\n padding: '6px 12px 10px',\r\n color: 'var(--dsw-alias-label-tertiary)',\r\n fontSize: 12,\r\n}\r\n\r\nconst EMPTY_STYLE: CSSProperties = {\r\n margin: '0 auto',\r\n padding: 24,\r\n maxWidth: 460,\r\n textAlign: 'center',\r\n color: 'var(--dsw-alias-label-tertiary)',\r\n lineHeight: 1.7,\r\n fontSize: 12,\r\n}\r\n\r\n/** Locale key per known slot status; unknown statuses fall back to the raw wire value. */\r\nconst STATUS_KEY: Record<string, string> = {\r\n open: 'slots.status.open',\r\n done: 'slots.status.done',\r\n dropped: 'slots.status.dropped',\r\n expired: 'slots.status.expired',\r\n}\r\n\r\n/**\r\n * One slot row: kind pill, title (full body rides the tooltip), status pill,\r\n * priority; body line underneath when the wire view carries it; refs as\r\n * resolved 名称简述 (record_id → title) with the raw refs on the tooltip, or\r\n * raw refs when the host has not resolved them.\r\n */\r\nfunction SlotRow(props: { slot: MemorySlotView; t: (key: string) => string }): ReactNode {\r\n const { slot, t } = props\r\n const statusLabel = STATUS_KEY[slot.status] === undefined ? slot.status : t(STATUS_KEY[slot.status])\r\n const title = slot.body === undefined ? slot.title : `${slot.title} — ${slot.body}`\r\n const refsLine = slot.refViews !== undefined\r\n ? slot.refViews.map((view) => view.title).join(' · ')\r\n : slot.refs !== undefined\r\n ? slot.refs.join(' · ')\r\n : undefined\r\n return createElement(\r\n 'li',\r\n { style: ROW_STYLE },\r\n createElement('span', { style: KIND_STYLE }, slot.kind),\r\n createElement(\r\n 'div',\r\n { style: { minWidth: 0, flex: '1 1 auto', display: 'flex', flexDirection: 'column', gap: 2 } },\r\n createElement('span', { style: TITLE_STYLE, title }, slot.title),\r\n slot.body !== undefined && createElement('p', { style: BODY_STYLE }, truncateBody(slot.body)),\r\n refsLine !== undefined\r\n && createElement(\r\n 'p',\r\n {\r\n style: REFS_STYLE,\r\n title: slot.refs !== undefined ? slot.refs.join('\\n') : undefined,\r\n },\r\n refsLine,\r\n ),\r\n ),\r\n createElement('span', { style: META_STYLE }, `P${slot.priority}`),\r\n createElement(\r\n 'span',\r\n { style: slot.status === 'open' ? STATUS_OPEN_STYLE : STATUS_SETTLED_STYLE },\r\n statusLabel,\r\n ),\r\n )\r\n}\r\n\r\n/**\r\n * A no-op unsubscribe source, kept as one identity so the absent-scope\r\n * subscription never thrashes (returns a no-op disposer, as the store\r\n * contract requires).\r\n */\r\nconst NO_SUBSCRIBE = (): (() => void) => () => {}\r\n\r\n/**\r\n * The snapshot served while no config scope is reachable — resolves to the\r\n * switch default (on) in `resolveShowMemorySlots`.\r\n */\r\nconst ABSENT_SNAPSHOT: SettingsScopeSnapshot<BriefClientConfig> = {\r\n status: 'unavailable',\r\n value: undefined,\r\n revision: undefined,\r\n writable: false,\r\n}\r\n\r\n/**\r\n * Subscribe to this plugin's entry config and resolve the section switch.\r\n *\r\n * The scope is resolved defensively (a host without `configForms` degrades to\r\n * the default instead of throwing inside a render) and subscribed with\r\n * `useSyncExternalStore`, so flipping the switch in settings updates the open\r\n * tab without a reload.\r\n *\r\n * @param ctx - the client root context handed to the tab body.\r\n * @returns whether the memory-slots section may render at all.\r\n */\r\nexport function useShowMemorySlots(ctx: Context | undefined): boolean {\r\n const scope = useMemo(() => {\r\n if (ctx === undefined) return undefined\r\n const configForms = ctx.get('configForms') as ConfigFormsLike | undefined\r\n if (configForms === undefined || typeof configForms.get !== 'function') return undefined\r\n return configForms.get<BriefClientConfig>('dsh-brief-sidebar')\r\n }, [ctx])\r\n\r\n const subscribe = useCallback(\r\n (listener: () => void) => scope?.subscribe(listener) ?? NO_SUBSCRIBE(),\r\n [scope],\r\n )\r\n const getSnapshot = useCallback(\r\n () => scope?.getSnapshot() ?? ABSENT_SNAPSHOT,\r\n [scope],\r\n )\r\n\r\n return resolveShowMemorySlots(useSyncExternalStore(subscribe, getSnapshot, getSnapshot))\r\n}\r\n\r\n/**\r\n * The memory-slots section. Both hooks run unconditionally (a conditional\r\n * hook order would break the moment the switch flips); the switch only\r\n * decides whether the resolved view paints.\r\n * @param props - translation, client context, session scope.\r\n */\r\nexport function MemorySlotsSection(props: MemorySlotsSectionProps): ReactNode {\r\n const { t, ctx, scope } = props\r\n\r\n const show = useShowMemorySlots(ctx)\r\n const view: MemorySlotsView | undefined = readSlots(\r\n useProjectionValue(ctx, scope?.sessionId, MEMORY_SLOTS_KEY),\r\n )\r\n\r\n // Switched off at the settings faces — the section is willed away, not\r\n // empty (an empty state would claim prime-memory answered when the user\r\n // simply closed the section).\r\n if (show === false) return null\r\n\r\n // Capability absent (prime-memory not in the composition, no frame yet) —\r\n // hide, don't nag; the memory panel remains the write surface.\r\n if (view === undefined) return null\r\n\r\n if (view.slots.length === 0) {\r\n return createElement(\r\n 'div',\r\n { style: SECTION_STYLE, 'data-dsh-brief-sidebar': 'memory-slots-section' },\r\n createElement('div', { style: HEADER_STYLE }, t('section.memorySlots')),\r\n createElement(\r\n 'div',\r\n { style: EMPTY_STYLE },\r\n createElement('div', { style: { marginBottom: 6, color: 'var(--dsw-alias-label-secondary)' } }, t('slots.empty')),\r\n createElement('div', null, t('slots.emptyHint')),\r\n ),\r\n )\r\n }\r\n\r\n return createElement(\r\n 'div',\r\n { style: SECTION_STYLE, 'data-dsh-brief-sidebar': 'memory-slots-section' },\r\n createElement('div', { style: HEADER_STYLE }, t('section.memorySlots')),\r\n createElement(\r\n 'ul',\r\n { style: LIST_STYLE },\r\n view.slots.map(slot => createElement(SlotRow, { key: slot.id, slot, t })),\r\n ),\r\n createElement(\r\n 'div',\r\n { style: TOTAL_STYLE },\r\n expandCount(t('slots.openCount'), view.openCount),\r\n ),\r\n )\r\n}\r\n","/**\r\n * The summary tab body: the session's at-a-glance state, composed of sections.\r\n *\r\n * Section order: 进展 (the todos board) → 产物 (produced files, codeplan\r\n * artifacts marked inline) → 记忆 (the active memory slots of the session,\r\n * served by the `memorySlots` projection dsh-prime-memory registers).\r\n * Each section owns its own data chain and its own gating contract; this\r\n * component owns only the shell.\r\n *\r\n * Two better-sidebar contracts are honoured:\r\n *\r\n * 1. **Height contract.** The tab body mounts inside a full-height column flex\r\n * host whose `.paneBody` is a definite-height BLOCK scroll container. The\r\n * root declares `height: 100%` + `min-height: 0`, and the scrolling element\r\n * is an inner div — not the root. Sections are non-scrolling slices with a\r\n * bottom border; the inner div scrolls them all.\r\n * 2. **`visible` pause.** Nothing is drawn while the tab is not the active\r\n * one, so a hidden tab does not re-render on every projection frame.\r\n */\r\nimport { createElement } from 'react'\r\nimport type { CSSProperties, ReactNode } from 'react'\r\nimport type { Context } from '@deepseek-ai/cordis'\r\nimport { TodoSection } from './TodoBoardTab'\r\nimport type { TodoScope } from './TodoBoardTab'\r\nimport { DeliverablesSection } from './summary/DeliverablesSection'\r\nimport { MemorySlotsSection } from './summary/MemorySlotsSection'\r\n\r\nexport interface SummaryTabProps {\r\n /** The DSH locale lookup, passed down from `apply`. */\r\n t: (key: string) => string\r\n /** The client root context (the tab body's only way to reach services). */\r\n ctx?: Context\r\n /** The session this tab is scoped to. */\r\n scope?: TodoScope\r\n /** Whether the tab is the active one AND the panel is open. */\r\n visible?: boolean\r\n}\r\n\r\nconst ROOT_STYLE: CSSProperties = {\r\n display: 'flex',\r\n flexDirection: 'column',\r\n height: '100%',\r\n minHeight: 0,\r\n background: 'var(--dsw-alias-bg-layer-1)',\r\n color: 'var(--dsw-alias-label-primary)',\r\n font: 'inherit',\r\n fontSize: 13,\r\n}\r\n\r\nconst SCROLL_STYLE: CSSProperties = { flex: 1, minHeight: 0, overflow: 'auto' }\r\n\r\n/**\r\n * The summary tab.\r\n * @param props - translation, client context, session scope, visibility.\r\n */\r\nexport function SummaryTab(props: SummaryTabProps): ReactNode {\r\n const { t, ctx, scope, visible = true } = props\r\n\r\n // A hidden tab renders NOTHING — not even the shell — so it never\r\n // re-renders on a projection frame while it is off screen.\r\n if (!visible) return null\r\n\r\n return createElement(\r\n 'div',\r\n { style: ROOT_STYLE, 'data-dsh-brief-sidebar': 'board' },\r\n createElement('div', { style: SCROLL_STYLE },\r\n createElement(TodoSection, { key: 'progress', t, ctx, scope }),\r\n createElement(DeliverablesSection, { key: 'deliverables', t, ctx, scope }),\r\n // 记忆分区(固定最底):入口开关(configForms `showMemorySlots`,默认开)\r\n // 与能力存在(dsh-prime-memory 的 `memorySlots` 投影)二者都满足才渲染,\r\n // 缺一即整段隐藏 —— 门控逻辑收敛在 MemorySlotsSection 内。\r\n createElement(MemorySlotsSection, { key: 'memory-slots', t, ctx, scope }),\r\n ),\r\n )\r\n}\r\n","/**\r\n * Native right-sidebar path: register the board tab directly into the DSH\r\n * host's own sidebar, with NO better-sidebar in sight. Available on host\r\n * generations that ship `dsh-client-ui-sidebar-right` (0.1.5+; verified by\r\n * slot-declaration forensics on 0.1.5-rc.3, 0.1.7-rc.2 and 0.2.0-rc.2).\r\n *\r\n * The host opens a tab type through a two-stage registration, and this module\r\n * copies better-sidebar's own native bridge EXACTLY — that plugin is the\r\n * shipped, field-proof consumer of the same seats:\r\n *\r\n * 1. Stage one, \"what the type IS\": `ctx.sidebarRightTabs.register({id, kind,\r\n * title, guide})` (a `SidebarRightTabDefinition`; page type — no `patterns`,\r\n * opened by kind). The returned disposer lives in our effect.\r\n * 2. Stage two, \"what the body draws\": a keyed registration under\r\n * `sidebar.right.pane.tab` whose `key` is the definition's `id`. The inject\r\n * callback receives the session id; everything else (our `t`, the root\r\n * `ctx`) rides a closure, and `SummaryTab` reads session data through\r\n * `ctx.get('sessions')` exactly as it does under better-sidebar.\r\n *\r\n * Degradations versus the better-sidebar path, accepted deliberately:\r\n * - no badge — the native chip has no badge affordance; the unfinished count\r\n * still shows inside the tab body.\r\n * - no `visible` pause — the host unmounts inactive bodies (`keepMounted`\r\n * defaults to false), which subsumes it.\r\n * - the open affordance is the guide-page capsule (`guide[]`), not a rail icon.\r\n *\r\n * Everything is read through `ctx.get` structural probes: the services are\r\n * absent on ≤0.1.2 hosts, so the caller must treat `undefined` as \"no native\r\n * surface\" and stay inert rather than crash. No import from\r\n * `@deepseek-ai/dsh-client-ui-sidebar-right` — a browser-only plugin keeps zero\r\n * build-time coupling to host internals, mirroring `use-projection.ts`.\r\n */\r\nimport type { ReactNode } from 'react'\r\nimport type { Context } from '@deepseek-ai/cordis'\r\nimport type { SlotsLike } from './brief/dock-shadow'\r\nimport { SummaryTab } from './SummaryTab'\r\n\r\n/** One guide-page capsule contributed by our type (structural `SidebarRightGuideEntry`). */\r\ninterface NativeGuideEntry {\r\n readonly id: string\r\n readonly order: number\r\n readonly title: () => string\r\n readonly description?: () => string\r\n}\r\n\r\n/** Stage-one shape (structural `SidebarRightTabDefinition`; only the page-type fields). */\r\ninterface NativeTabDefinition {\r\n readonly id: string\r\n readonly kind: string\r\n readonly title: (address: string) => string\r\n readonly guide?: readonly NativeGuideEntry[]\r\n}\r\n\r\n/** `ctx.sidebarRightTabs` — the only method we call. */\r\ninterface NativeTabRegistryLike {\r\n register(definition: NativeTabDefinition): () => void\r\n}\r\n\r\nexport type { NativeTabRegistryLike }\r\n\r\n/** The keyed seat a tab type registers its body under. */\r\nconst PANE_TAB_SEAT = 'sidebar.right.pane.tab'\r\n\r\n/**\r\n * Whether this host exposes the native right-sidebar tab system at all.\r\n *\r\n * Both services must answer: the registry stages the type, and a future\r\n * \"open from a button\" affordance goes through the controller face.\r\n * @param ctx - the client root context.\r\n */\r\nexport function nativeSidebarAvailable(ctx: Context): boolean {\r\n const registry = ctx.get('sidebarRightTabs') as NativeTabRegistryLike | undefined\r\n const controller = ctx.get('sidebarRight') as unknown\r\n return (\r\n registry !== undefined &&\r\n typeof registry.register === 'function' &&\r\n controller !== undefined &&\r\n ctx.get('slots') !== undefined\r\n )\r\n}\r\n\r\n/**\r\n * Register the board as a native right-sidebar tab type.\r\n *\r\n * The registry arrives as a parameter, read from the deferred `ctx.inject`\r\n * scope in `apply` — the 0.1.7+ strict-proxy ctx refuses a root `ctx.get`\r\n * of an undeclared service, so reaching for `sidebarRightTabs` from the root\r\n * context is what made the native seat silently no-op on 0.1.7 (the \"cannot\r\n * register directly\" observation). better-sidebar's own native bridge reads\r\n * it the same way: from the inject callback's scope.\r\n *\r\n * @param ctx - the client root context (for the declared `slots` service).\r\n * @param registry - the host tab-type registry from the injected scope.\r\n * @param deps - the stable ids and the translator from `apply`.\r\n * @returns a disposer that unregisters the seat first, then the type (the\r\n * registry refuses opens for a type whose body is already gone).\r\n */\r\nexport function registerNativeBoardTab(\r\n ctx: Context,\r\n registry: NativeTabRegistryLike | undefined,\r\n deps: { id: string; kind: string; order: number; t: (key: string) => string },\r\n): () => void {\r\n const slots = ctx.get('slots') as SlotsLike | undefined\r\n if (registry === undefined || typeof registry.register !== 'function' || slots === undefined) {\r\n return (): void => {}\r\n }\r\n\r\n // Stage 1: the type. Page kind — opened from the guide capsule by `kind`.\r\n const disposeType = registry.register({\r\n id: deps.id,\r\n kind: deps.kind,\r\n title: () => deps.t('tab.title'),\r\n guide: [\r\n {\r\n id: deps.id,\r\n order: deps.order,\r\n title: () => deps.t('tab.title'),\r\n description: () => deps.t('tab.desc'),\r\n },\r\n ],\r\n })\r\n\r\n // Stage 2: the body, keyed under the definition's own id. The payload\r\n // mirrors better-sidebar's `TabComponentProps` fields that SummaryTab reads;\r\n // `visible` is pinned true because the host unmounts hidden bodies anyway.\r\n const disposeSeat = slots.inject(PANE_TAB_SEAT, () =>\r\n slots.register(\r\n {\r\n name: PANE_TAB_SEAT,\r\n key: deps.id,\r\n inject: (sessionId: string) => ({\r\n t: deps.t,\r\n ctx,\r\n scope: { sessionId },\r\n visible: true,\r\n }),\r\n },\r\n SummaryTab as unknown as (props: unknown) => ReactNode,\r\n ),\r\n )\r\n\r\n return (): void => {\r\n try {\r\n disposeSeat()\r\n } finally {\r\n disposeType()\r\n }\r\n }\r\n}\r\n","/**\r\n * Tab glyph for the task board.\r\n *\r\n * Skin contract: plugin-drawn glyphs take their colour from the `--dsw-alias-*`\r\n * tokens — no colour literals anywhere in this file.\r\n */\r\nimport type { ReactNode } from 'react'\r\n\r\n/**\r\n * A checklist mark: three rows with ticks and text rails — the same status\r\n * vocabulary the board paints.\r\n * @param size - square edge in px (the host passes its own tab-icon size).\r\n */\r\nexport function BriefIcon(size: number): ReactNode {\r\n return (\r\n <svg\r\n width={size}\r\n height={size}\r\n viewBox=\"0 0 16 16\"\r\n fill=\"none\"\r\n aria-hidden=\"true\"\r\n focusable=\"false\"\r\n style={{ display: 'block', flex: 'none' }}\r\n >\r\n <rect\r\n x={1.6}\r\n y={2.4}\r\n width={12.8}\r\n height={11.2}\r\n rx={2}\r\n stroke=\"var(--dsw-alias-label-tertiary)\"\r\n strokeWidth={1.2}\r\n />\r\n <path\r\n d=\"M4.2 6.1l1 1 1.8-2\"\r\n stroke=\"var(--dsw-alias-state-success-primary)\"\r\n strokeWidth={1.3}\r\n strokeLinecap=\"round\"\r\n strokeLinejoin=\"round\"\r\n />\r\n <path\r\n d=\"M4.2 10.6l1 1 1.8-2\"\r\n stroke=\"var(--dsw-alias-label-tertiary)\"\r\n strokeWidth={1.3}\r\n strokeLinecap=\"round\"\r\n strokeLinejoin=\"round\"\r\n />\r\n <path\r\n d=\"M8.4 6.4h4M8.4 10.9h4\"\r\n stroke=\"var(--dsw-alias-label-tertiary)\"\r\n strokeWidth={1.2}\r\n strokeLinecap=\"round\"\r\n />\r\n </svg>\r\n )\r\n}\r\n","/**\r\n * Hiding the official todo dock.\r\n *\r\n * `conversation.input.dock` is a LIST slot, and its cells are keyed by each\r\n * entry's `id`. The slot core keeps one live entry per cell — entries are\r\n * sorted by `priority` ascending (then by `order`) and the first live entry of\r\n * a cell is the one the outlet renders, so a SECOND registration with the same\r\n * `id` at a LOWER priority shadows the first. The official todo entry\r\n * (`conversation-todo-dock` in `dsh-client-ui-conversation`) registers\r\n * `{ id: 'todo', order: 0 }` with the default priority 0, so\r\n * `{ id: 'todo', priority: -1 }` wins the cell and the official panel never\r\n * renders while this plugin is mounted.\r\n *\r\n * This is the same mechanism `dsh-input-traffic` uses on the sibling `queue`\r\n * cell (`{ id: 'queue', priority: -1 }`), so it is a proven seam on this host\r\n * generation, not a guess.\r\n *\r\n * Binding to a built-in id is a real coupling: if upstream renames the cell,\r\n * the shadow silently stops winning and the official dock simply reappears\r\n * (fail-open — the data is still shown, just twice). `slots.inject` is used\r\n * rather than a bare `register` because the slot is declared by a built-in\r\n * entry whose lifetime we do not control: `inject` waits for the declaration\r\n * and re-installs the entry if the declaration is ever recreated.\r\n *\r\n * Renders nothing on purpose: the todo UI now lives in this plugin's sidebar\r\n * tab. Return a summary chip here instead to keep a one-line entry point in the\r\n * composer band.\r\n */\r\nimport type { ReactNode } from 'react'\r\nimport type { Context } from '@deepseek-ai/cordis'\r\n\r\n/** The built-in cell id of the official todo dock. */\r\nexport const TODO_CELL_ID = 'todo'\r\n\r\n/** Lower than the built-in's default 0 — the winner of the `todo` cell. */\r\nexport const TODO_SHADOW_PRIORITY = -1\r\n\r\n/** Structural view of the client slots service (avoids a second type graph). */\r\nexport interface SlotsLike {\r\n inject(key: string, callback: () => (() => void) | undefined): () => void\r\n register(options: Record<string, unknown>, component: (props: unknown) => ReactNode): () => void\r\n}\r\n\r\n/** The replacement entry: deliberately empty. */\r\nfunction TodoDockShadow(): ReactNode {\r\n return null\r\n}\r\n\r\n/**\r\n * Register the shadowing entry.\r\n *\r\n * The caller wraps this in `ctx.effect(...)` so the returned disposer rides the\r\n * plugin fiber: unloading the plugin restores the official dock.\r\n *\r\n * @param ctx - the client root context.\r\n * @returns the disposer, or undefined when the slots service is unreachable.\r\n */\r\nexport function registerTodoDockShadow(ctx: Context): (() => void) | undefined {\r\n const slots = ctx.get('slots') as SlotsLike | undefined\r\n if (slots === undefined || typeof slots.inject !== 'function') return undefined\r\n\r\n return slots.inject('conversation.input.dock', () =>\r\n slots.register(\r\n {\r\n name: 'conversation.input.dock',\r\n id: TODO_CELL_ID,\r\n order: 0,\r\n priority: TODO_SHADOW_PRIORITY,\r\n registrant: 'dsh-brief-sidebar',\r\n },\r\n TodoDockShadow,\r\n ),\r\n )\r\n}\r\n","/**\r\n * BriefSettingsCard — the brief-sidebar settings card.\r\n *\r\n * One component mounted at TWO seats, fed by the SAME inject factory (one\r\n * source of truth, the dsh-thinking-levels arrangement):\r\n *\r\n * - the plugin-family settings section (「起子插件设置」, the `settings.section`\r\n * entry dsh-thinking-levels registers; this plugin contributes its card to\r\n * the `dsh-family.tab` child slot — the inject idles harmlessly when the\r\n * family section is absent);\r\n * - the Plugins-page configuration card (`plugins.bundle.config`, keyed by\r\n * the package name, rendered on the bundle's detail page).\r\n *\r\n * The card binds this plugin's entry config through the `configForms` service\r\n * and renders the fields the summary tab reads. Every change commits\r\n * immediately through the scope (no staged form), so a flip applies to the\r\n * open summary tab on its next render.\r\n *\r\n * The `t` binder is injected by the factory (the same closure the tab uses) —\r\n * the card does not rely on the framework's locale props, so it renders the\r\n * active language on every host generation. Kept dependency-free beyond\r\n * react: inline styles over `--dsw-alias-*` tokens only (no CSS modules, no\r\n * hex/rgba literals — the purity spec enforces the token contract).\r\n */\r\nimport { useCallback, useState, useSyncExternalStore } from 'react'\r\nimport { createElement } from 'react'\r\nimport type { CSSProperties, ChangeEvent, ReactNode } from 'react'\r\nimport type { BriefClientConfig, SettingsScope, SettingsScopeSnapshot } from './scope-face'\r\n\r\n/** A no-op unsubscribe source, kept as one identity so effects never thrash. */\r\nconst NO_SUBSCRIBE = (): (() => void) => () => {}\r\n\r\n/** The snapshot served while no scope is reachable. */\r\nconst ABSENT: SettingsScopeSnapshot<BriefClientConfig> = {\r\n status: 'unavailable',\r\n value: undefined,\r\n revision: undefined,\r\n writable: false,\r\n}\r\n\r\n/** Injected face: the entry scope, plus the plugin's own binder. */\r\nexport interface BriefSettingsCardInjected {\r\n /** `configForms.get('dsh-brief-sidebar')` — may be unavailable on odd hosts. */\r\n scope?: SettingsScope<BriefClientConfig>\r\n /** The plugin's locale binder (applied-time closure over the locale service). */\r\n t: (key: string) => string\r\n}\r\n\r\nexport type BriefSettingsCardProps = BriefSettingsCardInjected\r\n\r\n/* ── shared row styling (inline; keeps the client bundle CSS-free) ──────── */\r\n\r\nconst CARD_STYLE: CSSProperties = {\r\n border: '1px solid var(--dsw-alias-border-l2)',\r\n background: 'var(--dsw-alias-bg-layer-3)',\r\n borderRadius: 12,\r\n transition: 'border-color 0.16s, background 0.16s',\r\n}\r\n\r\nconst HEADER_STYLE: CSSProperties = {\r\n appearance: 'none',\r\n width: '100%',\r\n font: 'inherit',\r\n color: 'inherit',\r\n textAlign: 'left',\r\n cursor: 'pointer',\r\n background: 'none',\r\n border: 0,\r\n borderRadius: 12,\r\n display: 'flex',\r\n alignItems: 'center',\r\n gap: 12,\r\n padding: '14px 16px',\r\n}\r\n\r\nconst HEADER_TEXT_STYLE: CSSProperties = { flex: '1 1 0%', minWidth: 0 }\r\n\r\nconst NAME_STYLE: CSSProperties = {\r\n fontSize: 14,\r\n fontWeight: 600,\r\n color: 'var(--dsw-alias-label-primary)',\r\n}\r\n\r\nconst DESC_STYLE: CSSProperties = {\r\n color: 'var(--dsw-alias-label-tertiary)',\r\n fontSize: 13,\r\n lineHeight: 1.5,\r\n}\r\n\r\nconst CHEVRON_STYLE = (open: boolean): CSSProperties => ({\r\n color: 'var(--dsw-alias-label-tertiary)',\r\n flex: '0 0 auto',\r\n transition: 'transform 0.16s',\r\n transform: open ? 'rotate(180deg)' : 'none',\r\n})\r\n\r\nconst BODY_STYLE: CSSProperties = { padding: '12px 16px' }\r\n\r\nconst ROW_STYLE: CSSProperties = {\r\n display: 'flex',\r\n alignItems: 'flex-start',\r\n justifyContent: 'space-between',\r\n gap: 12,\r\n padding: '8px 0',\r\n fontSize: 13,\r\n lineHeight: '20px',\r\n}\r\n\r\nconst ROW_TEXT_STYLE: CSSProperties = {\r\n display: 'flex',\r\n flexDirection: 'column',\r\n gap: 4,\r\n minWidth: 0,\r\n}\r\n\r\nconst LABEL_STYLE: CSSProperties = { margin: 0, color: 'var(--dsw-alias-label-primary)' }\r\n\r\nconst HINT_STYLE: CSSProperties = {\r\n margin: 0,\r\n fontSize: 12,\r\n lineHeight: '18px',\r\n color: 'var(--dsw-alias-label-tertiary)',\r\n}\r\n\r\nconst SWITCH_STYLE: CSSProperties = { flexShrink: 0, margin: 0, cursor: 'pointer' }\r\n\r\n/**\r\n * The settings card body. `scope` is optional on purpose: the inject factory\r\n * always provides it, but a host whose `configForms` face degrades must not\r\n * crash the settings page — the card renders the unavailable note and keeps\r\n * its header.\r\n */\r\nexport function BriefSettingsCard(props: BriefSettingsCardProps): ReactNode {\r\n const { t, scope } = props\r\n const [open, setOpen] = useState(true)\r\n\r\n const snapshot: SettingsScopeSnapshot<BriefClientConfig> | undefined = useSyncExternalStore(\r\n useCallback((listener: () => void) => scope?.subscribe(listener) ?? NO_SUBSCRIBE(), [scope]),\r\n useCallback(() => scope?.getSnapshot() ?? ABSENT, [scope]),\r\n useCallback(() => scope?.getSnapshot() ?? ABSENT, [scope]),\r\n )\r\n const unavailable = scope === undefined || snapshot.status === 'unavailable'\r\n const readonly = unavailable || !snapshot.writable\r\n const checked = snapshot.value?.showMemorySlots !== false\r\n\r\n return createElement(\r\n 'div',\r\n { style: CARD_STYLE, 'data-dsh-brief-sidebar': 'settings-card' },\r\n createElement(\r\n 'button',\r\n { type: 'button', 'aria-expanded': open, style: HEADER_STYLE, onClick: () => { setOpen(current => !current) } },\r\n createElement(\r\n 'span',\r\n { style: HEADER_TEXT_STYLE },\r\n createElement('div', { style: NAME_STYLE }, t('settings.title')),\r\n createElement('div', { style: DESC_STYLE }, t('settings.desc')),\r\n ),\r\n createElement(\r\n 'svg',\r\n { width: 16, height: 16, viewBox: '0 0 16 16', 'aria-hidden': true, style: CHEVRON_STYLE(open) },\r\n createElement('path', {\r\n d: 'M4 6l4 4 4-4',\r\n fill: 'none',\r\n stroke: 'currentColor',\r\n strokeWidth: 1.5,\r\n strokeLinecap: 'round',\r\n strokeLinejoin: 'round',\r\n }),\r\n ),\r\n ),\r\n open && createElement(\r\n 'div',\r\n { style: BODY_STYLE },\r\n unavailable\r\n ? createElement('p', { style: HINT_STYLE }, t('settings.unavailable'))\r\n : createElement(\r\n 'div',\r\n { style: ROW_STYLE },\r\n createElement(\r\n 'div',\r\n { style: ROW_TEXT_STYLE },\r\n createElement('label', { htmlFor: 'dsh-brief-sidebar-show-memory-slots', style: LABEL_STYLE }, t('settings.memorySlots')),\r\n createElement('p', { style: HINT_STYLE }, t('settings.memorySlotsHint')),\r\n ),\r\n createElement('input', {\r\n id: 'dsh-brief-sidebar-show-memory-slots',\r\n type: 'checkbox',\r\n checked,\r\n disabled: readonly,\r\n style: SWITCH_STYLE,\r\n onChange: (event: ChangeEvent<HTMLInputElement>) => {\r\n void scope?.set('showMemorySlots', event.currentTarget.checked)\r\n },\r\n }),\r\n ),\r\n !unavailable && !snapshot.writable && createElement('p', { style: HINT_STYLE }, t('settings.readonly')),\r\n ),\r\n )\r\n}\r\n","/**\r\n * Browser half: the task board tab, plus the shadow that hides the official\r\n * composer-band todo panel.\r\n *\r\n * Registration order matters and is deliberate:\r\n *\r\n * 1. **Dictionaries first.** Every later registration can render translated\r\n * copy, and a dictionary that lands after the first paint would show raw\r\n * keys for a frame.\r\n * 2. **Sidebar surface, deferred.** The board needs a sidebar to live in:\r\n * better-sidebar when it is installed (richer: badge + rail icon), else the\r\n * host's own right-sidebar tab system (0.1.5+ `sidebarRightTabs` +\r\n * `sidebar.right.pane.tab`). Both are acquired through the deferred\r\n * `ctx.inject` because neither service is ordered against this apply —\r\n * an immediate `ctx.get` was the 0.1.2 inert bug. With neither — ≤0.1.2\r\n * hosts without better-sidebar — the plugin stays sidebar-inert: the\r\n * official panel keeps rendering and the shadow never installs.\r\n * 3. **Shadow the official dock.** Deliberately BEFORE the tab registration:\r\n * the panel is the only other carrier of the list, so the replacement has to\r\n * exist in the same activation.\r\n * 4. **Register the tab.**\r\n * 5. **Register the settings cards.** The memory-section switch gets TWO\r\n * faces from ONE component + ONE inject factory: a card in the plugin-family\r\n * settings section (「起子插件设置」, hosted by dsh-thinking-levels) and the\r\n * Plugins-page configuration card. `configForms` resolves through a\r\n * deferred inject (the service may start after apply — the same lateness\r\n * the composer model panel in dsh-thinking-levels guards against); on a\r\n * host without it the cards never register and the section gate falls back\r\n * to its default (on).\r\n *\r\n * Every registration rides `ctx.effect(fn, label)` so its disposer is revoked on\r\n * fiber teardown (HMR / disable), which is also what makes the shadow\r\n * reversible: unload the plugin and the official dock renders again. The\r\n * settings cards are the family-proven exception: they ride the deferred\r\n * `configForms` inject whose disposers are not held (same as\r\n * dsh-thinking-levels' composer panel registration).\r\n *\r\n * Contract notes for the two surfaces touched here:\r\n * - better-sidebar tabs are hosted by DSH's native right Sidebar, so the tab\r\n * body receives `TabComponentProps` (`ctx` + `scope` + `visible`) and NOT the\r\n * framework's session-scoped slot props.\r\n * - `conversation.input.dock` is the DSH slot the official todo panel occupies;\r\n * see `brief/dock-shadow.tsx` for why a lower priority wins the cell.\r\n */\r\nimport { createElement } from 'react'\r\nimport type { ReactNode } from 'react'\r\nimport type { Context } from '@deepseek-ai/cordis'\r\nimport type { BetterSidebarService, TabComponentProps } from 'dsh-better-sidebar/client/service'\r\nimport { registerNativeBoardTab } from './native-sidebar'\r\nimport type { NativeTabRegistryLike } from './native-sidebar'\r\nimport { SummaryTab } from './SummaryTab'\r\nimport { BriefIcon } from './BriefIcon'\r\nimport { NS, dictionaries } from './locales'\r\nimport { readTodos, unfinishedCount } from './brief/board'\r\nimport { registerTodoDockShadow } from './brief/dock-shadow'\r\nimport type { SlotsLike } from './brief/dock-shadow'\r\nimport { TODOS_KEY } from './brief/use-todos'\r\nimport { projectionSnapshot } from './use-projection'\r\nimport { BriefSettingsCard } from './settings-card'\r\nimport type { BriefSettingsCardProps } from './settings-card'\r\nimport type { BriefClientConfig, ConfigFormsLike } from './scope-face'\r\n\r\n/**\r\n * Tab type id. Package-prefixed so it cannot collide with a built-in type.\r\n * Unchanged since the 0.1.0 board tab: the summary tab REPLACES the board tab\r\n * in place, so an opened or pinned tab survives the upgrade.\r\n */\r\nexport const TAB_ID = 'dsh-brief-sidebar:board'\r\n\r\n/**\r\n * Tab kind on the native host-sidebar path (`openTab`/guide dispatch key).\r\n * Only the native registration uses it; better-sidebar keeps addressing the\r\n * tab by {@link TAB_ID}.\r\n */\r\nexport const TAB_KIND = 'brief-board'\r\n\r\n/**\r\n * Position in the host's new-tab guide.\r\n *\r\n * Built-ins on this host generation sit at editor 10 / git 20 / subagent 30 /\r\n * sidechat 35 / terminal 40 / browser 50. 15 places a per-session task board\r\n * right after the editor, which is where a reader looks for session state.\r\n */\r\nexport const TAB_ORDER = 15\r\n\r\n/** Services required before `apply` runs. */\r\n// Generation-neutral list only: betterSidebar/locale ride ctx.get soft-reads in\r\n// apply (both absent on 0.1.0/0.1.1 — declaring them pends the whole client\r\n// entry, web boot refuses to render; playbook §1).\r\n// betterSidebar 经 ctx.get 软读(缺席 inert);locale 全线 rc 皆在(0.1.0-rc.2 起)。\r\n// 第三方插件服务不入顶层 inject——缺席格 entry 永久 pending,web boot 拒渲染整树。\r\nexport const inject = ['slots', 'locale']\r\n\r\n/**\r\n * Structural view of the DSH locale service — only the two members this plugin\r\n * calls. Declared locally on purpose: importing the real type would pull the\r\n * whole client type graph into a browser-only plugin for two signatures.\r\n */\r\ninterface LocaleLike {\r\n register(ns: string, dicts: Record<string, Record<string, string>>): () => void\r\n bind(ns: string): (key: string) => string\r\n}\r\n\r\n/**\r\n * An effect body must return a disposer (cordis rejects `undefined` with a\r\n * `TypeError`), so a path that could not register anything returns this no-op\r\n * instead of nothing.\r\n */\r\nconst NOOP = (): void => {}\r\n\r\n/** Console prefix for the environment probe and registration attribution. */\r\nconst LOG = '[dsh-brief-sidebar]'\r\n\r\n/**\r\n * Run a registration thunk, attributing a thrown failure to the console\r\n * instead of the cordis effect void (a bare throw inside `ctx.effect` leaves\r\n * the field reader with an empty sidebar and no clue which arm died).\r\n */\r\nfunction guarded(label: string, run: () => (() => void) | undefined | void): () => void {\r\n try {\r\n const dispose = run()\r\n return typeof dispose === 'function' ? (dispose as () => void) : NOOP\r\n } catch (error) {\r\n console.error(`${LOG} ${label} failed`, error)\r\n return NOOP\r\n }\r\n}\r\n\r\n/**\r\n * Browser-face apply.\r\n *\r\n * @param ctx - the client root context.\r\n */\r\nexport function apply(ctx: Context): void {\r\n // 1. Plugin-owned dictionaries, in one registration.\r\n const locale: LocaleLike | undefined = ctx.get('locale')\r\n if (locale !== undefined) {\r\n ctx.effect(() => locale.register(NS, dictionaries), 'dsh-brief-sidebar: dictionaries')\r\n }\r\n\r\n /**\r\n * Translate one of our keys. `bind` returns a cached function that reads the\r\n * ACTIVE locale at call time, so a language switch needs no re-apply; an\r\n * unknown namespace or key falls back to the key itself rather than throwing\r\n * inside a render.\r\n */\r\n const t = (key: string): string => {\r\n if (locale === undefined) return key\r\n try {\r\n return locale.bind(NS)(key) || key\r\n } catch {\r\n return key\r\n }\r\n }\r\n\r\n // 2. Sidebar surface, acquired DEFERRED. better-sidebar is a third-party\r\n // plugin with no ordering guarantee against us, and the native tab\r\n // registry is a host service that can publish after this apply too — an\r\n // immediate `ctx.get` misses either one and stays inert forever (the\r\n // 0.1.2 field failure). Both arms ride the deferred optional\r\n // `ctx.inject` (the browser-cdp / configForms precedent). The host\r\n // registry usually wins the race on 0.1.5+ (it publishes before the\r\n // third-party sidebar), so the state is NOT a one-shot latch: when\r\n // better-sidebar arrives after a native seat, it HANDS OVER — the\r\n // native registration is released and the board re-seats on the bar,\r\n // which is the rail and manager the user actually sees (the 0.1.5\r\n // \"invisible tab\" failure was a native seat under a sidebar UI that\r\n // replaced the official rail). On a bare native host (0.1.7 farm cell)\r\n // no handover ever fires.\r\n let surface: 'none' | 'bar' | 'native' = 'none'\r\n let shadowDone = false\r\n let nativeDisposer: (() => void) | undefined\r\n\r\n /** Hide the official composer-band panel (reversible on unload), once. */\r\n const shadowDock = (): void => {\r\n if (shadowDone) return\r\n shadowDone = true\r\n ctx.effect(() => registerTodoDockShadow(ctx) ?? NOOP, 'dsh-brief-sidebar: dock shadow')\r\n }\r\n\r\n const registerOnBar = (bar: BetterSidebarService): void => {\r\n if (surface === 'bar') return\r\n if (surface === 'native') {\r\n const release = nativeDisposer\r\n nativeDisposer = undefined\r\n try {\r\n release?.()\r\n } catch { /* a half-torn-down native seat must not block the handover */ }\r\n console.info(`${LOG} surface=bar (handed over from native)`)\r\n } else {\r\n console.info(`${LOG} surface=bar`)\r\n }\r\n surface = 'bar'\r\n shadowDock()\r\n ctx.effect(\r\n () =>\r\n guarded('registerTab', () =>\r\n bar.registerTab({\r\n id: TAB_ID,\r\n title: () => t('tab.title'),\r\n description: () => t('tab.desc'),\r\n icon: (size: number) => BriefIcon(size),\r\n order: TAB_ORDER,\r\n // Shorthand for `dedupeKey: () => id`: reopening focuses the existing tab.\r\n single: true,\r\n /**\r\n * Unfinished count on the tab strip. Called on every tab-bar render, so\r\n * it reads one snapshot and allocates no subscription; a completed list\r\n * shows no badge at all.\r\n */\r\n badge: (badgeCtx, badgeScope) => {\r\n const todos = readTodos(projectionSnapshot(badgeCtx, badgeScope.sessionId, TODOS_KEY))\r\n if (todos === undefined) return null\r\n const pending = unfinishedCount(todos)\r\n return pending > 0 ? pending : null\r\n },\r\n component: (props: TabComponentProps) =>\r\n createElement(SummaryTab, {\r\n t,\r\n ctx: props.ctx,\r\n scope: props.scope,\r\n visible: props.visible,\r\n }),\r\n }),\r\n ),\r\n 'dsh-brief-sidebar: tab',\r\n )\r\n }\r\n\r\n const registerOnNative = (registry: NativeTabRegistryLike | undefined): void => {\r\n if (surface !== 'none') return\r\n surface = 'native'\r\n console.info(`${LOG} surface=native`)\r\n shadowDock()\r\n ctx.effect(\r\n () => {\r\n nativeDisposer = guarded('native tab', () => registerNativeBoardTab(ctx, registry, { id: TAB_ID, kind: TAB_KIND, order: TAB_ORDER, t }))\r\n return () => {\r\n nativeDisposer = undefined\r\n }\r\n },\r\n 'dsh-brief-sidebar: native tab',\r\n )\r\n }\r\n\r\n // Arm 1: better-sidebar, immediate read first (the common case where the\r\n // sidebar started before us), deferred retry otherwise.\r\n let bar: BetterSidebarService | undefined\r\n try {\r\n bar = ctx.get('betterSidebar')\r\n } catch {\r\n bar = undefined\r\n }\r\n if (bar !== undefined) {\r\n registerOnBar(bar)\r\n } else {\r\n ;(ctx as unknown as {\r\n inject: (deps: string[], cb: (scope: { betterSidebar?: BetterSidebarService }) => void) => void\r\n }).inject(['betterSidebar'], (scope) => {\r\n if (scope.betterSidebar !== undefined) registerOnBar(scope.betterSidebar)\r\n })\r\n }\r\n\r\n // Arm 2: the host's own tab registry (0.1.5+). Never fires on ≤0.1.2. The\r\n // registry is read from the INJECTED scope — a root ctx.get of this\r\n // undeclared service is denied by the strict proxy (the 0.1.7 silent no-op).\r\n ;(ctx as unknown as {\r\n inject: (deps: string[], cb: (scope: { sidebarRightTabs?: NativeTabRegistryLike }) => void) => void\r\n }).inject(['sidebarRightTabs'], (scope) => {\r\n registerOnNative(scope.sidebarRightTabs)\r\n })\r\n\r\n // 5. The settings cards (see the module doc): ONE component + ONE inject\r\n // factory at TWO seats. The entry ids follow the family convention —\r\n // `dsh-family.tab` contributors key by short id (session-guard →\r\n // 'session-guard'), `plugins.bundle.config` keys by package name.\r\n // `configForms` rides a deferred inject: the service lives in the\r\n // settings module, which may start after this apply; the callback simply\r\n // never fires on a host without it (the gate then reads its default).\r\n const slots = ctx.get('slots') as SlotsLike | undefined\r\n ;(ctx as unknown as {\r\n inject: (deps: string[], cb: (scope: { configForms?: ConfigFormsLike }) => void) => void\r\n }).inject(['configForms'], (scope) => {\r\n const configForms = scope.configForms\r\n if (slots === undefined || configForms === undefined || typeof configForms.get !== 'function') return\r\n\r\n /** Same face for both seats: the entry scope plus the applied-time binder. */\r\n const injectFactory = (): BriefSettingsCardProps => ({\r\n t,\r\n scope: configForms.get<BriefClientConfig>('dsh-brief-sidebar'),\r\n })\r\n /** The slot core hands the component `unknown` props (registry typing). */\r\n const component = BriefSettingsCard as unknown as (props: unknown) => ReactNode\r\n\r\n slots.inject('dsh-family.tab', () =>\r\n slots.register(\r\n {\r\n name: 'dsh-family.tab',\r\n id: 'brief-sidebar',\r\n order: 30,\r\n label: () => t('settings.title'),\r\n inject: injectFactory,\r\n },\r\n component,\r\n ),\r\n )\r\n slots.inject('plugins.bundle.config', () =>\r\n slots.register(\r\n {\r\n name: 'plugins.bundle.config',\r\n key: 'dsh-brief-sidebar',\r\n inject: injectFactory,\r\n },\r\n component,\r\n ),\r\n )\r\n })\r\n\r\n // One-line environment snapshot: what this apply could see at mount time.\r\n // Cross-reference with the `surface=` line to tell \"arm never fired\" from\r\n // \"arm fired and registration threw\" (guarded logs that part).\r\n try {\r\n let nativeTabsAtApply = false\r\n try {\r\n nativeTabsAtApply = ctx.get('sidebarRightTabs') !== undefined\r\n } catch {\r\n nativeTabsAtApply = false /* strict proxy denies the undeclared read */\r\n }\r\n console.info(`${LOG} env ${JSON.stringify({\r\n locale: locale !== undefined,\r\n barAtApply: bar !== undefined,\r\n nativeTabsAtApply,\r\n })}`)\r\n } catch { /* console may be absent in a stripped host */ }\r\n}\r\n"],"mappings":"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"}