dsh-brief-sidebar 0.3.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/README.md +178 -0
- package/cordis.patch.yml +18 -0
- package/dsh.plugin.json +16 -0
- package/lib/client.js +960 -0
- package/lib/client.js.map +1 -0
- package/lib/index.mjs +5620 -0
- package/lib/index.mjs.map +1 -0
- package/lib/types/client/BriefIcon.d.ts +13 -0
- package/lib/types/client/SummaryTab.d.ts +18 -0
- package/lib/types/client/TodoBoardTab.d.ts +31 -0
- package/lib/types/client/brief/board.d.ts +50 -0
- package/lib/types/client/brief/dock-shadow.d.ts +49 -0
- package/lib/types/client/brief/use-todos.d.ts +13 -0
- package/lib/types/client/file-open.d.ts +50 -0
- package/lib/types/client/index.d.ts +23 -0
- package/lib/types/client/locales.d.ts +53 -0
- package/lib/types/client/summary/DeliverablesSection.d.ts +18 -0
- package/lib/types/client/summary/deliverables.d.ts +40 -0
- package/lib/types/client/use-projection.d.ts +54 -0
- package/lib/types/index.d.ts +27 -0
- package/lib/types/projection/deliverables-fold.d.ts +85 -0
- package/lib/types/projection/keys.d.ts +25 -0
- package/lib/types/projection/register.d.ts +9 -0
- package/package.json +94 -0
- package/src/client/BriefIcon.tsx +56 -0
- package/src/client/SummaryTab.tsx +73 -0
- package/src/client/TodoBoardTab.tsx +188 -0
- package/src/client/brief/board.ts +104 -0
- package/src/client/brief/dock-shadow.tsx +74 -0
- package/src/client/brief/use-todos.ts +27 -0
- package/src/client/file-open.ts +108 -0
- package/src/client/index.tsx +144 -0
- package/src/client/locales.ts +78 -0
- package/src/client/summary/DeliverablesSection.tsx +192 -0
- package/src/client/summary/deliverables.ts +93 -0
- package/src/client/use-projection.ts +117 -0
- package/src/index.ts +32 -0
- package/src/projection/deliverables-fold.ts +206 -0
- package/src/projection/keys.ts +27 -0
- package/src/projection/register.ts +99 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","names":["useMemo","useCallback","useSyncExternalStore","useMemo","SECTION_STYLE","HEADER_STYLE","LIST_STYLE","ROW_STYLE","createElement","createElement","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/SummaryTab.tsx","../src/client/BriefIcon.tsx","../src/client/brief/dock-shadow.tsx","../src/client/index.tsx"],"sourcesContent":["/**\n * The `todos` projection — reading and classifying it.\n *\n * The Host computes this projection (`dsh-tool-todo` registers the key) and\n * pushes finished whole values; the client never folds it. This module is the\n * read side of that contract and is deliberately PURE — no React, no context,\n * no IO — so the mapping can be tested directly.\n */\n/** The projection's three-state lifecycle (`TodoItem` in `dsh-tool-todo`). */\nexport type TodoStatus = 'pending' | 'in_progress' | 'completed'\n\n/** One entry of the `todos` projection. */\nexport interface TodoItem {\n readonly content: string\n readonly status: TodoStatus\n}\n\nconst STATUSES: readonly TodoStatus[] = ['pending', 'in_progress', 'completed']\n\n/** Locale key per status. Kept here so this module owns the vocabulary. */\nconst STATUS_KEY: Record<TodoStatus, string> = {\n pending: 'status.pending',\n in_progress: 'status.inProgress',\n completed: 'status.completed',\n}\n\n/** Locale key for one status label. */\nexport function statusKey(status: TodoStatus): string {\n return STATUS_KEY[status]\n}\n\n/**\n * Narrow a raw projection value into a todo list.\n *\n * Three outcomes are kept apart on purpose, mirroring the store's own contract:\n * - `undefined` — the capability is ABSENT (no session, the host unit is\n * unmounted, or no baseline/frame has carried the key yet);\n * - `[]` — the projection is present and the list is empty (`null` on the wire);\n * - a list — real entries.\n *\n * A malformed entry is dropped rather than thrown on: the projection is a\n * trusted Host value, but a shape drift must not blank the whole board.\n *\n * @param value - the raw `unknown` snapshot of the `todos` face.\n * @returns the validated list, or `undefined` when the capability is absent.\n */\nexport function readTodos(value: unknown): readonly TodoItem[] | undefined {\n if (value === null) return []\n if (!Array.isArray(value)) return undefined\n\n const items: TodoItem[] = []\n for (const raw of value) {\n if (raw === null || typeof raw !== 'object') continue\n const { content, status } = raw as { content?: unknown; status?: unknown }\n if (typeof content !== 'string') continue\n if (typeof status !== 'string' || !isStatus(status)) continue\n items.push({ content, status })\n }\n return items\n}\n\n/** Whether a string is one of the three lifecycle values. */\nfunction isStatus(value: string): value is TodoStatus {\n return (STATUSES as readonly string[]).includes(value)\n}\n\n/** Per-status counts over one list. */\nexport interface TodoCounts {\n readonly total: number\n readonly completed: number\n readonly inProgress: number\n readonly pending: number\n}\n\n/** Count a todo list by status. */\nexport function countTodos(todos: readonly TodoItem[]): TodoCounts {\n let completed = 0\n let inProgress = 0\n let pending = 0\n for (const item of todos) {\n if (item.status === 'completed') completed += 1\n else if (item.status === 'in_progress') inProgress += 1\n else pending += 1\n }\n return { total: todos.length, completed, inProgress, pending }\n}\n\n/** How many entries are not finished — the tab badge. */\nexport function unfinishedCount(todos: readonly TodoItem[]): number {\n const counts = countTodos(todos)\n return counts.inProgress + counts.pending\n}\n\n/**\n * The one-line progress summary, composed from parts so no interpolation\n * machinery is needed (the locale binder is a plain key → string lookup).\n * Mirrors the official dock's own label.\n */\nexport function progressLine(counts: TodoCounts, t: (key: string) => string): string {\n const parts = [`${t('status.completed')} ${counts.completed}/${counts.total}`]\n if (counts.inProgress > 0) parts.push(`${t('status.inProgress')} ${counts.inProgress}`)\n if (counts.pending > 0) parts.push(`${t('status.pending')} ${counts.pending}`)\n return parts.join(' · ')\n}\n","/**\n * Reading a live session projection from the client Session Controller.\n *\n * better-sidebar tab bodies are NOT rendered inside a DSH slot, so the\n * framework's `useProjection` standard prop is not available to them. The\n * equivalent face is reachable directly: the client Session Controller is a\n * cordis service on the root context, and each session binding exposes its\n * projections store (`SessionFace.projections.faceOf(key)`), which is the very\n * observable `useProjection` resolves. Since it is a bare\n * `{ getSnapshot, subscribe }` source, `useSyncExternalStore` binds it as-is —\n * no local mirror, no folding, and the value reference only changes when the\n * Host pushes a frame.\n *\n * Everything here is resolved defensively: a host without the Session\n * Controller, or a session that is not open any more, degrades to \"capability\n * absent\" (undefined) rather than throwing inside a render.\n *\n * This module is key-agnostic; the per-projection knowledge (which key, how to\n * narrow the raw value) lives with the feature modules that call it.\n */\nimport { useCallback, useMemo, useSyncExternalStore } from 'react'\nimport type { Context } from '@deepseek-ai/cordis'\n\n/** Structural view of a projection face (`ObservableSnapshot<unknown>`). */\nexport interface ProjectionFace {\n getSnapshot(): unknown\n subscribe(listener: () => void): () => void\n}\n\n/** Structural view of one session binding's projection store. */\nexport interface ProjectionsLike {\n faceOf(key: string): ProjectionFace\n}\n\n/** Structural view of the client Session Controller face this plugin uses. */\nexport interface SessionsLike {\n binding(id: string): { readonly session: { readonly projections: ProjectionsLike } } | undefined\n}\n\n/** A no-op unsubscribe, kept as one identity so effects never thrash. */\nconst NO_SUBSCRIBE = (): void => {}\n\n/** A source that never has a value — the absent-capability face. */\nconst ABSENT: ProjectionFace = { getSnapshot: () => undefined, subscribe: () => NO_SUBSCRIBE }\n\n/**\n * Whether a value really is a projection face.\n *\n * The store contract promises an identity-stable face for every key, so this\n * never fires against a healthy host. It exists because the fallback is worse\n * than the check: `useSyncExternalStore` would throw on a face-less key, and a\n * thrown render is a blank sidebar rather than a diagnosable \"unavailable\".\n */\nfunction isFace(value: unknown): value is ProjectionFace {\n if (value === null || typeof value !== 'object') return false\n const face = value as Partial<ProjectionFace>\n return typeof face.getSnapshot === 'function' && typeof face.subscribe === 'function'\n}\n\n/**\n * Resolve one projection face for one session.\n *\n * `ctx.get('sessions')` is the safe accessor (a direct `ctx.sessions` read can\n * throw on a context that does not carry the service yet).\n *\n * @param ctx - the client root context handed to the tab body.\n * @param sessionId - the tab's session scope; undefined means \"no session\".\n * @param key - the projection key to resolve.\n * @returns the projection face, or {@link ABSENT} when unreachable.\n */\nexport function resolveFace(ctx: Context | undefined, sessionId: string | undefined, key: string): ProjectionFace {\n if (ctx === undefined || sessionId === undefined) return ABSENT\n const sessions = ctx.get('sessions') as SessionsLike | undefined\n if (sessions === undefined || typeof sessions.binding !== 'function') return ABSENT\n\n const binding = sessions.binding(sessionId)\n const projections = binding?.session?.projections\n if (projections === undefined || typeof projections.faceOf !== 'function') return ABSENT\n\n const face: unknown = projections.faceOf(key)\n return isFace(face) ? face : ABSENT\n}\n\n/**\n * Read one frame of a projection without subscribing.\n *\n * The one-shot sibling of {@link useProjectionValue}, for consumers that are\n * not React components — the tab badge is a plain function called on every\n * tab-bar render, so it must neither hold a subscription nor allocate one.\n *\n * @param ctx - the client root context.\n * @param sessionId - the session whose projection is read.\n * @param key - the projection key to read.\n * @returns the raw snapshot: `undefined` while the capability is absent; the\n * semantics of any other value belong to the projection's own contract.\n */\nexport function projectionSnapshot(ctx: Context | undefined, sessionId: string | undefined, key: string): unknown {\n return resolveFace(ctx, sessionId, key).getSnapshot()\n}\n\n/**\n * Subscribe to one session projection by key.\n *\n * @param ctx - the client root context.\n * @param sessionId - the session whose projection is shown.\n * @param key - the projection key to subscribe to.\n * @returns the raw snapshot value, identity-stable across frames the host did\n * not change. Narrowing is the caller's job.\n */\nexport function useProjectionValue(ctx: Context | undefined, sessionId: string | undefined, key: string): unknown {\n const face = useMemo(() => resolveFace(ctx, sessionId, key), [ctx, sessionId, key])\n\n const subscribe = useCallback((listener: () => void) => face.subscribe(listener), [face])\n const getSnapshot = useCallback(() => face.getSnapshot(), [face])\n\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot)\n}\n","/**\n * Reading the live `todos` projection — the thin, todo-specific wrapper around\n * the generic face resolver (`../use-projection`). The projection key is\n * registered by `dsh-tool-todo`; the host pushes finished whole values and the\n * client never folds it. Narrowing lives in `./board` (`readTodos`).\n */\nimport { useMemo } from 'react'\nimport type { Context } from '@deepseek-ai/cordis'\nimport { readTodos } from './board'\nimport type { TodoItem } from './board'\nimport { useProjectionValue } from '../use-projection'\n\n/** The projection key registered by `dsh-tool-todo`. */\nexport const TODOS_KEY = 'todos'\n\n/**\n * Subscribe to one session's todo list.\n *\n * @param ctx - the client root context.\n * @param sessionId - the session whose projection is shown.\n * @returns the list, `[]` for a present-but-empty projection, or `undefined`\n * while the capability is absent.\n */\nexport function useTodos(ctx: Context | undefined, sessionId: string | undefined): readonly TodoItem[] | undefined {\n const raw = useProjectionValue(ctx, sessionId, TODOS_KEY)\n return useMemo(() => readTodos(raw), [raw])\n}\n","/**\n * The progress section: the `todos` projection drawn as one part of the\n * summary tab.\n *\n * The official composer-band panel is shadowed by this plugin (see\n * `brief/dock-shadow.tsx`), so the projection is read here and rendered as an\n * ordinary React view — no Canvas, no cross-package dependency. The full-height\n * shell and the scroll container live on the summary tab (`../SummaryTab`);\n * this component renders only its own slice and is the one section whose\n * absent state must stay VISIBLE: the official dock is hidden while this\n * plugin is mounted, so \"unavailable\" is a statement the reader needs.\n *\n * Colours come from `--dsw-alias-*` tokens only (skin contract), and the status\n * palette uses tokens that actually exist in the shipping theme:\n * `state-success-primary` / `state-business-primary` / `label-tertiary`.\n */\nimport { createElement } from 'react'\nimport type { CSSProperties, ReactNode } from 'react'\nimport type { Context } from '@deepseek-ai/cordis'\nimport { countTodos, progressLine, statusKey } from './brief/board'\nimport type { TodoItem, TodoStatus } from './brief/board'\nimport { useTodos } from './brief/use-todos'\n\n/** The session scope better-sidebar hands a tab body. */\nexport interface TodoScope {\n readonly sessionId?: string\n}\n\nexport interface TodoSectionProps {\n /** The DSH locale lookup, passed down from `apply`. */\n t: (key: string) => string\n /** The client root context (the tab body's only way to reach services). */\n ctx?: Context\n /** The session this tab is scoped to. */\n scope?: TodoScope\n}\n\n/** Status → token colour for the pill's text and border. */\nconst STATUS_COLOR: Record<TodoStatus, string> = {\n completed: 'var(--dsw-alias-state-success-primary)',\n in_progress: 'var(--dsw-alias-state-business-primary)',\n pending: 'var(--dsw-alias-label-tertiary)',\n}\n\nconst SECTION_STYLE: CSSProperties = {\n flexShrink: 0,\n borderBottom: '1px solid var(--dsw-alias-border-secondary)',\n}\n\nconst HEADER_STYLE: CSSProperties = {\n padding: '10px 12px 0',\n color: 'var(--dsw-alias-label-secondary)',\n fontSize: 12,\n fontWeight: 600,\n}\n\nconst PROGRESS_STYLE: CSSProperties = {\n padding: '6px 12px 10px',\n color: 'var(--dsw-alias-label-secondary)',\n fontSize: 12,\n}\n\nconst LIST_STYLE: CSSProperties = {\n listStyle: 'none',\n margin: 0,\n padding: '6px 0',\n display: 'flex',\n flexDirection: 'column',\n}\n\nconst ROW_STYLE: CSSProperties = {\n display: 'flex',\n alignItems: 'flex-start',\n gap: 8,\n padding: '6px 12px',\n}\n\n/** Pill style, coloured per status. */\nfunction pillStyle(status: TodoStatus): CSSProperties {\n return {\n flexShrink: 0,\n color: STATUS_COLOR[status],\n border: `1px solid ${STATUS_COLOR[status]}`,\n borderRadius: 999,\n padding: '0 6px',\n fontSize: 11,\n lineHeight: '16px',\n whiteSpace: 'nowrap',\n }\n}\n\n/** Task text: finished entries read as done, the working one as active. */\nfunction contentStyle(status: TodoStatus): CSSProperties {\n return {\n minWidth: 0,\n overflowWrap: 'anywhere',\n color:\n status === 'completed'\n ? 'var(--dsw-alias-label-tertiary)'\n : status === 'in_progress'\n ? 'var(--dsw-alias-label-primary)'\n : 'var(--dsw-alias-label-secondary)',\n textDecoration: status === 'completed' ? 'line-through' : 'none',\n }\n}\n\n/** Centred message block used by every non-list state. */\nexport function Notice(props: { title: string; detail?: string }): ReactNode {\n return (\n <div\n style={{\n margin: '0 auto',\n padding: 24,\n maxWidth: 460,\n textAlign: 'center',\n color: 'var(--dsw-alias-label-tertiary)',\n lineHeight: 1.7,\n fontSize: 12,\n }}\n >\n <div style={{ marginBottom: 6, color: 'var(--dsw-alias-label-secondary)' }}>{props.title}</div>\n {props.detail === undefined ? null : <div>{props.detail}</div>}\n </div>\n )\n}\n\n/** One task row. */\nfunction TodoRow(props: { item: TodoItem; t: (key: string) => string }): ReactNode {\n const { item, t } = props\n return createElement(\n 'li',\n { style: ROW_STYLE },\n createElement('span', { style: pillStyle(item.status) }, t(statusKey(item.status))),\n createElement('span', { style: contentStyle(item.status) }, item.content),\n )\n}\n\n/**\n * The progress section. The subscription is unconditional (a plain listener,\n * not IO); the three-state contract of the projection is preserved verbatim.\n * @param props - translation, client context, session scope.\n */\nexport function TodoSection(props: TodoSectionProps): ReactNode {\n const { t, ctx, scope } = props\n\n const todos = useTodos(ctx, scope?.sessionId)\n\n if (todos === undefined) {\n // `undefined` is the store's \"capability absent\": no session, or the host's\n // todo unit is unmounted. Say so rather than showing an empty board.\n return createElement(\n 'div',\n { style: SECTION_STYLE, 'data-dsh-brief-sidebar': 'progress-section' },\n createElement('div', { style: HEADER_STYLE }, t('section.progress')),\n createElement(Notice, { title: t('board.unavailable'), detail: t('board.unavailableHint') }),\n )\n }\n\n if (todos.length === 0) {\n return createElement(\n 'div',\n { style: SECTION_STYLE, 'data-dsh-brief-sidebar': 'progress-section' },\n createElement('div', { style: HEADER_STYLE }, t('section.progress')),\n createElement(Notice, { title: t('board.empty'), detail: t('board.emptyHint') }),\n )\n }\n\n const counts = countTodos(todos)\n\n return createElement(\n 'div',\n { style: SECTION_STYLE, 'data-dsh-brief-sidebar': 'progress-section' },\n createElement('div', { style: HEADER_STYLE }, t('section.progress')),\n createElement('div', { style: PROGRESS_STYLE }, progressLine(counts, t)),\n createElement(\n 'ul',\n { style: LIST_STYLE },\n todos.map((item, index) => createElement(TodoRow, { key: `${index}:${item.content}`, item, t })),\n ),\n )\n}\n\n/**\n * Backwards-compatible alias for the pre-summary export name. The component\n * no longer owns the full-height shell — that moved to `SummaryTab` — so the\n * alias exists only to keep import sites honest during the transition.\n */\nexport const TodoBoardTab = TodoSection\n","/**\n * The projection key this plugin contributes to the session-projection\n * registry, and its wire view.\n *\n * The key is namespaced with the plugin name on purpose: the registry refuses\n * to share a key across differing `stateVersion`s, so a collision with a\n * future host-side deliverables unit would be a hard load error, not a silent\n * shadow. The view type is declared structurally (the plugin has no compile\n * dependency on `@deepseek-ai/dsh-session-projection`); the registry accepts\n * any string key at runtime.\n */\n\n/** The projection key registered by this plugin's host half. */\nexport const DELIVERABLES_KEY = 'dshSummaryDeliverables'\n\n/** Client view of the produced-file fold (JSON-safe). */\nexport interface DeliverablesView {\n /** Paths produced in the latest turn, first-seen order (capped). */\n readonly latest: {\n readonly turn: number\n readonly paths: readonly string[]\n } | null\n /** Distinct produced paths session-wide, most-recent-first (capped). */\n readonly sessionPaths: readonly string[]\n /** Distinct produced paths session-wide, uncapped — the truthful total. */\n readonly sessionTotal: number\n}\n","/**\n * Opening a produced file's preview in the right Sidebar — the same channel\n * the conversation flow uses for its produced-file chips and inline mentions\n * (ui-chat `openFile` → `ctx.sidebarRight.openResource` with a\n * `dsh-resource://file/…` address).\n *\n * The address grammar is ported verbatim from the harness's browser-safe\n * `@deepseek-ai/dsh-util-workspace-path` (`file-address.ts` / `index.ts`),\n * because the plugin does not compile against that package: a relative path,\n * or an absolute path inside the session's workspace, is addressed under the\n * session's scope (`dsh-resource://file/session/<id>/<path>`); an absolute\n * path elsewhere keeps its absolute spelling in the same session's address.\n * Which tab type claims the address is the Sidebar's decision, not ours.\n *\n * The `sidebarRight` service is consumed structurally and treated as OPTIONAL:\n * where it is absent the section renders plain rows instead of links, the same\n * graceful degradation as the deliverables projection itself.\n */\nimport type { Context } from '@deepseek-ai/cordis'\n\n/** Structural view of the right-Sidebar service this plugin needs. */\nexport interface SidebarRightLike {\n openResource(url: string, options?: { params?: { line?: number } }): void\n}\n\n/** Structural view of the sessions service slice the opener reads. */\ninterface SessionsListLike {\n list?: { getSnapshot(): { byId?: Record<string, { cwd?: string }> } }\n}\n\n/** The address scheme every file resource opens with. */\nconst FILE_ADDRESS_PREFIX = 'dsh-resource://file/'\n\n/** Whether a path uses a Windows drive or UNC prefix. */\nfunction isWindowsStylePath(value: string): boolean {\n return /^[A-Za-z]:[/\\\\]/.test(value) || value.startsWith('\\\\\\\\')\n}\n\n/**\n * Whether a path is absolute in either spelling the Host accepts: POSIX (`/a/b`)\n * or Windows drive or UNC.\n */\nexport function isAbsoluteWorkspacePath(path: string): boolean {\n return path.startsWith('/') || isWindowsStylePath(path)\n}\n\n/** Component-encode one id or path segment, keeping `:` literal for drive letters. */\nfunction encodeSegment(segment: string): string {\n return encodeURIComponent(segment).replace(/%3A/gi, ':')\n}\n\n/** Encode a `/`-separated path segment by segment. */\nfunction encodePath(path: string): string {\n return path.split('/').map(encodeSegment).join('/')\n}\n\n/**\n * Build the address of a file read through one Session.\n * @param sessionId - the Session whose Host workspace resolves the path.\n * @param path - absolute or workspace-relative path; backslashes are normalized\n * to `/`, and leading `./` prefixes are dropped.\n */\nfunction sessionFileAddress(sessionId: string, path: string): string {\n const normalized = path.replace(/\\\\/g, '/').replace(/^(?:\\.\\/)+/, '')\n return `${FILE_ADDRESS_PREFIX}session/${encodeSegment(sessionId)}/${encodePath(normalized)}`\n}\n\n/**\n * The address for a path as a caller holds it.\n * @param sessionId - the Session the path is read in.\n * @param cwd - that Session's workspace root, when known.\n * @param path - absolute or workspace-relative path, in either separator spelling.\n */\nexport function fileAddressFor(sessionId: string, cwd: string | undefined, path: string): string {\n const normalized = path.replace(/\\\\/g, '/')\n if (!isAbsoluteWorkspacePath(normalized)) return sessionFileAddress(sessionId, normalized)\n const root = cwd === undefined ? '' : cwd.replace(/\\\\/g, '/').replace(/\\/+$/, '')\n if (root !== '' && normalized === root) return sessionFileAddress(sessionId, '')\n if (root !== '' && normalized.startsWith(`${root}/`)) {\n return sessionFileAddress(sessionId, normalized.slice(root.length + 1))\n }\n return sessionFileAddress(sessionId, normalized)\n}\n\n/**\n * Build a sidebar file opener for one session, or `undefined` when the\n * environment cannot open previews (no `sidebarRight` service, no session).\n * The cwd is re-read per call inside the opener, so a workspace switch between\n * renders is picked up on the next click.\n *\n * @param ctx - the client root context.\n * @param sessionId - the session whose workspace resolves paths.\n * @returns an opener that routes a produced path to the Sidebar preview.\n */\nexport function sidebarFileOpener(\n ctx: Context | undefined,\n sessionId: string | undefined,\n): ((path: string) => void) | undefined {\n if (ctx === undefined || sessionId === undefined) return undefined\n const bar = ctx.get('sidebarRight') as SidebarRightLike | undefined\n if (bar === undefined || typeof bar.openResource !== 'function') return undefined\n\n return (path: string) => {\n const sessions = ctx.get('sessions') as SessionsListLike | undefined\n const cwd = sessions?.list?.getSnapshot?.().byId?.[sessionId]?.cwd\n bar.openResource(fileAddressFor(sessionId, cwd, path))\n }\n}\n","/**\n * The `dshSummaryDeliverables` projection — reading and classifying it.\n *\n * The Host computes this projection (this plugin's own node half registers the\n * unit) and pushes finished whole values; the client never folds it. This\n * module is the read side of that contract and is deliberately PURE — no\n * React, no context, no IO — so the narrowing and the codeplan classification\n * can be tested directly.\n */\nimport type { DeliverablesView } from '../../projection/keys'\n\n/**\n * Narrow a raw projection value into a deliverables view.\n *\n * Two outcomes are kept apart on purpose, mirroring the store's contract:\n * - `undefined` — the capability is ABSENT (the host half never registered —\n * e.g. an older composition — or no frame carried the key yet);\n * - a view — the projection is live. A view with `latest: null` and\n * `sessionTotal: 0` IS the empty state; the fold starts publishing as soon\n * as the unit registers, so \"present but empty\" is a real value here, not a\n * sentinel.\n *\n * @param value - the raw `unknown` snapshot of the deliverables face.\n * @returns the validated view, or `undefined` when the capability is absent.\n */\nexport function readDeliverables(value: unknown): DeliverablesView | undefined {\n if (value === null || typeof value !== 'object' || Array.isArray(value)) return undefined\n const raw = value as { latest?: unknown; sessionPaths?: unknown; sessionTotal?: unknown }\n if (raw.latest === undefined || raw.sessionPaths === undefined || raw.sessionTotal === undefined) {\n return undefined\n }\n if (!isBucket(raw.latest) || !Array.isArray(raw.sessionPaths) || typeof raw.sessionTotal !== 'number') {\n return undefined\n }\n const paths = raw.sessionPaths.filter((path): path is string => typeof path === 'string')\n return {\n latest: raw.latest === null ? null : { turn: raw.latest.turn, paths: raw.latest.paths },\n sessionPaths: paths,\n sessionTotal: raw.sessionTotal,\n }\n}\n\n/** Whether a value is a well-formed latest-turn bucket (or explicitly null). */\nfunction isBucket(value: unknown): value is { turn: number; paths: readonly string[] } | null {\n if (value === null) return true\n if (typeof value !== 'object') return false\n const bucket = value as { turn?: unknown; paths?: unknown }\n return typeof bucket.turn === 'number' && Array.isArray(bucket.paths)\n}\n\n/**\n * The path prefix that identifies a codeplan artifact. The codeplan skill\n * writes its four fixed artifacts under `$workspace\\.agents\\plans\\<任务名>\\`;\n * produced paths may spell the separators either way.\n */\nconst CODEPLAN_SEGMENT = '.agents/plans/'\n\n/**\n * Normalize path separators so one spelling of the codeplan prefix matches.\n * Only classification uses this; the displayed path keeps the tool's exact\n * spelling.\n */\nfunction normalized(path: string): string {\n return path.replaceAll('\\\\', '/')\n}\n\n/** Whether a produced path is a codeplan artifact. */\nexport function isCodeplanPath(path: string): boolean {\n return normalized(path).includes(CODEPLAN_SEGMENT)\n}\n\n/**\n * Split a codeplan path into its task name and file name.\n *\n * @param path - a produced path; need not actually be a codeplan artifact.\n * @returns task and file names, or `null` when the path is not under the\n * codeplan plans directory (or names no file beneath the task folder).\n */\nexport function splitCodeplanPath(path: string): { task: string; file: string } | null {\n const marker = normalized(path).indexOf(CODEPLAN_SEGMENT)\n if (marker < 0) return null\n const rest = normalized(path).slice(marker + CODEPLAN_SEGMENT.length)\n const slash = rest.indexOf('/')\n if (slash <= 0 || slash === rest.length - 1) return null\n return { task: rest.slice(0, slash), file: rest.slice(slash + 1) }\n}\n\n/** The final path segment, under either separator. */\nexport function basename(path: string): string {\n const normalizedPath = normalized(path)\n const slash = normalizedPath.lastIndexOf('/')\n return slash < 0 ? normalizedPath : normalizedPath.slice(slash + 1)\n}\n","/**\n * Plugin-owned i18n dictionaries.\n *\n * Consumer plugins must NOT reach into better-sidebar's internal `t()` or its\n * `betterSidebar` dictionary namespace, so this plugin registers its own\n * namespace through the DSH `locale` service. `dsh-brief-sidebar` is outside the\n * `LocaleNamespaceMap` merge table, which is exactly what the untyped\n * `register(ns, dicts)` overload is for — but the dictionaries themselves stay\n * strictly paired: `zh` is the key-set source of truth and `en` is typed as\n * `Record<keyof typeof zh, string>`, so a missing or extra English key is a\n * COMPILE error rather than a runtime fallback to the raw key.\n *\n * Every key referenced anywhere in `src/**` must exist in both dictionaries;\n * `tests/purity.spec.ts` asserts that, so a typo in a `t('…')` call cannot\n * ship as an untranslated literal.\n */\n\n/** The namespace owned by this plugin (own vocabulary, own lifecycle). */\nexport const NS = 'dsh-brief-sidebar'\n\n/** Simplified Chinese dictionary — the key-set source of truth. */\nexport const zh = {\n 'tab.title': '概要',\n 'tab.desc': '当前会话的任务、产物与规划文件(只读)',\n 'section.progress': '进展',\n 'section.deliverables': '产物',\n 'board.empty': '当前没有任务',\n 'board.emptyHint': '模型调用 todo_write 之后,任务会出现在这里。',\n 'board.unavailable': '任务暂不可用',\n 'board.unavailableHint': '这个会话还没有收到 todo 数据;宿主推来第一帧后会自动显示。',\n 'status.pending': '待处理',\n 'status.inProgress': '进行中',\n 'status.completed': '已完成',\n 'deliverables.empty': '本回合暂无改动文件',\n 'deliverables.emptyHint': '会话里成功写入或修改文件后,它们会实时出现在这里。',\n 'deliverables.sessionTotal': '本会话共 {count} 个文件',\n 'deliverable.codeplanTag': '规划',\n}\n\n/** The key union: what a `t('…')` call may name inside this namespace. */\nexport type BriefKey = keyof typeof zh\n\n/** English dictionary, checked complete against the zh key set. */\nexport const en: Record<BriefKey, string> = {\n 'tab.title': 'Summary',\n 'tab.desc': 'Tasks, produced files and plan artifacts of this session (read-only)',\n 'section.progress': 'Progress',\n 'section.deliverables': 'Produced files',\n 'board.empty': 'No tasks yet',\n 'board.emptyHint': 'Tasks show up here once the model calls todo_write.',\n 'board.unavailable': 'Tasks unavailable',\n 'board.unavailableHint':\n 'This session has not received any todo data yet; the board fills in on the first frame.',\n 'status.pending': 'Pending',\n 'status.inProgress': 'In progress',\n 'status.completed': 'Done',\n 'deliverables.empty': 'No files changed in this turn',\n 'deliverables.emptyHint': 'Files written or edited successfully in the session appear here live.',\n 'deliverables.sessionTotal': '{count} files in this session',\n 'deliverable.codeplanTag': 'Plan',\n}\n\n/**\n * Both locales in the shape the locale service consumes. Registered in ONE\n * call: the registry rejects a duplicate `(namespace, locale)` pair and the\n * per-locale form would leave a half-registered namespace if the second call\n * threw.\n */\nexport const dictionaries: Record<string, Record<string, string>> = { zh, en }\n\n/**\n * Expand the one `{count}` placeholder the deliverables totals line uses. The\n * locale binder is a plain key → string lookup (no interpolation machinery),\n * mirroring how `progressLine` composes its summary from parts.\n */\nexport function expandCount(template: string, count: number): string {\n return template.replace('{count}', String(count))\n}\n","/**\n * The deliverables section: the `dshSummaryDeliverables` projection drawn as\n * one part of the summary tab.\n *\n * Unlike the progress section this is an OPTIONAL capability: where the host\n * half never registered the unit (older composition, key absent), the section\n * hides entirely — absence is not an error state here, and the official\n * \"本次产出\" row in the conversation flow still covers the turn-end view.\n * When the projection is live, the latest turn's produced paths update\n * mid-turn: every successful mutation result publishes a frame, so the reader\n * sees files appear while the turn is still running, which is exactly the gap\n * the official turn-tail row has.\n *\n * Codeplan artifacts (paths under `.agents/plans/<任务名>/`) are ordinary\n * produced entries — they are marked with a pill, not split into a separate\n * section.\n *\n * Colours come from `--dsw-alias-*` tokens only (skin contract).\n */\nimport { createElement } from 'react'\nimport type { CSSProperties, ReactNode } from 'react'\nimport type { Context } from '@deepseek-ai/cordis'\nimport { DELIVERABLES_KEY } from '../../projection/keys'\nimport { useProjectionValue } from '../use-projection'\nimport { sidebarFileOpener } from '../file-open'\nimport { basename, readDeliverables, splitCodeplanPath } from './deliverables'\nimport { expandCount } from '../locales'\n\nexport interface DeliverablesSectionProps {\n /** The DSH locale lookup, passed down from `apply`. */\n t: (key: string) => string\n /** The client root context (the tab body's only way to reach services). */\n ctx?: Context\n /** The session this tab is scoped to. */\n scope?: { readonly sessionId?: string }\n}\n\nconst SECTION_STYLE: CSSProperties = {\n flexShrink: 0,\n borderBottom: '1px solid var(--dsw-alias-border-secondary)',\n}\n\nconst HEADER_STYLE: CSSProperties = {\n padding: '10px 12px 0',\n color: 'var(--dsw-alias-label-secondary)',\n fontSize: 12,\n fontWeight: 600,\n}\n\nconst LIST_STYLE: CSSProperties = {\n listStyle: 'none',\n margin: 0,\n padding: '6px 0',\n display: 'flex',\n flexDirection: 'column',\n}\n\nconst ROW_STYLE: CSSProperties = {\n display: 'flex',\n alignItems: 'flex-start',\n gap: 8,\n padding: '6px 12px',\n}\n\nconst TOTAL_STYLE: CSSProperties = {\n padding: '6px 12px 10px',\n color: 'var(--dsw-alias-label-tertiary)',\n fontSize: 12,\n}\n\nconst PATH_STYLE: CSSProperties = {\n minWidth: 0,\n overflowWrap: 'anywhere',\n color: 'var(--dsw-alias-label-secondary)',\n}\n\n/** Row-as-button reset: the row itself is the click target, content unchanged. */\nconst BUTTON_STYLE: CSSProperties = {\n ...PATH_STYLE,\n background: 'none',\n border: 'none',\n font: 'inherit',\n fontSize: 'inherit',\n color: 'var(--dsw-alias-label-secondary)',\n padding: 0,\n textAlign: 'left',\n cursor: 'pointer',\n}\n\n/** The codeplan pill: same form as the status pills, tertiary emphasis. */\nconst TAG_STYLE: CSSProperties = {\n flexShrink: 0,\n color: 'var(--dsw-alias-label-tertiary)',\n border: '1px solid var(--dsw-alias-label-tertiary)',\n borderRadius: 999,\n padding: '0 6px',\n fontSize: 11,\n lineHeight: '16px',\n whiteSpace: 'nowrap',\n}\n\n/**\n * One produced-file row. When the sidebar opener is available the row is a\n * button that routes the path to the Sidebar preview (the same channel the\n * conversation's produced-file chips use); otherwise it degrades to a plain\n * span. A codeplan artifact carries the pill either way.\n */\nfunction PathRow(props: {\n path: string\n t: (key: string) => string\n open?: (path: string) => void\n}): ReactNode {\n const { path, t, open } = props\n const plan = splitCodeplanPath(path)\n const title = plan === null ? path : `${t('deliverable.codeplanTag')}: ${plan.task} · ${path}`\n const label = basename(path)\n const content = plan === null ? null : createElement('span', { style: TAG_STYLE }, t('deliverable.codeplanTag'))\n if (open === undefined) {\n return createElement(\n 'li',\n { style: ROW_STYLE },\n content,\n createElement('span', { style: PATH_STYLE, title }, label),\n )\n }\n return createElement(\n 'li',\n { style: ROW_STYLE },\n content,\n createElement('button', { style: BUTTON_STYLE, title, onClick: () => { open(path) } }, label),\n )\n}\n\n/**\n * The deliverables section. The subscription is unconditional (a plain\n * listener, not IO).\n * @param props - translation, client context, session scope.\n */\nexport function DeliverablesSection(props: DeliverablesSectionProps): ReactNode {\n const { t, ctx, scope } = props\n\n const view = readDeliverables(useProjectionValue(ctx, scope?.sessionId, DELIVERABLES_KEY))\n\n // Capability absent — the optional unit never registered. Hide, don't nag.\n if (view === undefined) return null\n\n // Optional click-to-preview: rows degrade to plain text where the sidebar\n // service is absent (the opener resolves the session cwd per click).\n const open = sidebarFileOpener(ctx, scope?.sessionId)\n\n const latestPaths = view.latest?.paths ?? []\n\n if (latestPaths.length === 0 && view.sessionTotal === 0) {\n return createElement(\n 'div',\n { style: SECTION_STYLE, 'data-dsh-brief-sidebar': 'deliverables-section' },\n createElement('div', { style: HEADER_STYLE }, t('section.deliverables')),\n createElement(\n 'div',\n {\n style: {\n margin: '0 auto',\n padding: 24,\n maxWidth: 460,\n textAlign: 'center',\n color: 'var(--dsw-alias-label-tertiary)',\n lineHeight: 1.7,\n fontSize: 12,\n },\n },\n createElement('div', { style: { marginBottom: 6, color: 'var(--dsw-alias-label-secondary)' } }, t('deliverables.empty')),\n createElement('div', null, t('deliverables.emptyHint')),\n ),\n )\n }\n\n return createElement(\n 'div',\n { style: SECTION_STYLE, 'data-dsh-brief-sidebar': 'deliverables-section' },\n createElement('div', { style: HEADER_STYLE }, t('section.deliverables')),\n createElement(\n 'ul',\n { style: LIST_STYLE },\n latestPaths.map(path => createElement(PathRow, { key: path, path, t, open })),\n ),\n createElement(\n 'div',\n { style: TOTAL_STYLE },\n expandCount(t('deliverables.sessionTotal'), view.sessionTotal),\n ),\n )\n}\n","/**\n * The summary tab body: the session's at-a-glance state, composed of sections.\n *\n * Section order: 进展 (the todos board) → 产物 (produced files, codeplan\n * artifacts marked inline) → the reserved memory-recall slot (see below).\n * Each section owns its own data chain and its own three-state contract; this\n * component owns only the shell.\n *\n * Two better-sidebar contracts are honoured:\n *\n * 1. **Height contract.** The tab body mounts inside a full-height column flex\n * host whose `.paneBody` is a definite-height BLOCK scroll container. The\n * root declares `height: 100%` + `min-height: 0`, and the scrolling element\n * is an inner div — not the root. Sections are non-scrolling slices with a\n * bottom border; the inner div scrolls them all.\n * 2. **`visible` pause.** Nothing is drawn while the tab is not the active\n * one, so a hidden tab does not re-render on every projection frame.\n */\nimport { createElement } from 'react'\nimport type { CSSProperties, ReactNode } from 'react'\nimport type { Context } from '@deepseek-ai/cordis'\nimport { TodoSection } from './TodoBoardTab'\nimport type { TodoScope } from './TodoBoardTab'\nimport { DeliverablesSection } from './summary/DeliverablesSection'\n\nexport interface SummaryTabProps {\n /** The DSH locale lookup, passed down from `apply`. */\n t: (key: string) => string\n /** The client root context (the tab body's only way to reach services). */\n ctx?: Context\n /** The session this tab is scoped to. */\n scope?: TodoScope\n /** Whether the tab is the active one AND the panel is open. */\n visible?: boolean\n}\n\nconst ROOT_STYLE: CSSProperties = {\n display: 'flex',\n flexDirection: 'column',\n height: '100%',\n minHeight: 0,\n background: 'var(--dsw-alias-bg-layer-1)',\n color: 'var(--dsw-alias-label-primary)',\n font: 'inherit',\n fontSize: 13,\n}\n\nconst SCROLL_STYLE: CSSProperties = { flex: 1, minHeight: 0, overflow: 'auto' }\n\n/**\n * The summary tab.\n * @param props - translation, client context, session scope, visibility.\n */\nexport function SummaryTab(props: SummaryTabProps): ReactNode {\n const { t, ctx, scope, visible = true } = props\n\n // A hidden tab renders NOTHING — not even the shell — so it never\n // re-renders on a projection frame while it is off screen.\n if (!visible) return null\n\n return createElement(\n 'div',\n { style: ROOT_STYLE, 'data-dsh-brief-sidebar': 'board' },\n createElement('div', { style: SCROLL_STYLE },\n createElement(TodoSection, { key: 'progress', t, ctx, scope }),\n createElement(DeliverablesSection, { key: 'deliverables', t, ctx, scope }),\n // 记忆召回插槽(远期,固定最底):概要从两区扩为三区时,记忆召回分区\n // 排在这里。系统中没有默认记忆,数据链等记忆类插件注册自己的投影 key\n // 后经同一个 `faceOf` 通道接入(`useProjectionValue`),本期不渲染。\n null,\n ),\n )\n}\n","/**\n * Tab glyph for the task board.\n *\n * Skin contract: plugin-drawn glyphs take their colour from the `--dsw-alias-*`\n * tokens — no colour literals anywhere in this file.\n */\nimport type { ReactNode } from 'react'\n\n/**\n * A checklist mark: three rows with ticks and text rails — the same status\n * vocabulary the board paints.\n * @param size - square edge in px (the host passes its own tab-icon size).\n */\nexport function BriefIcon(size: number): ReactNode {\n return (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n aria-hidden=\"true\"\n focusable=\"false\"\n style={{ display: 'block', flex: 'none' }}\n >\n <rect\n x={1.6}\n y={2.4}\n width={12.8}\n height={11.2}\n rx={2}\n stroke=\"var(--dsw-alias-label-tertiary)\"\n strokeWidth={1.2}\n />\n <path\n d=\"M4.2 6.1l1 1 1.8-2\"\n stroke=\"var(--dsw-alias-state-success-primary)\"\n strokeWidth={1.3}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n <path\n d=\"M4.2 10.6l1 1 1.8-2\"\n stroke=\"var(--dsw-alias-label-tertiary)\"\n strokeWidth={1.3}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n <path\n d=\"M8.4 6.4h4M8.4 10.9h4\"\n stroke=\"var(--dsw-alias-label-tertiary)\"\n strokeWidth={1.2}\n strokeLinecap=\"round\"\n />\n </svg>\n )\n}\n","/**\n * Hiding the official todo dock.\n *\n * `conversation.input.dock` is a LIST slot, and its cells are keyed by each\n * entry's `id`. The slot core keeps one live entry per cell — entries are\n * sorted by `priority` ascending (then by `order`) and the first live entry of\n * a cell is the one the outlet renders, so a SECOND registration with the same\n * `id` at a LOWER priority shadows the first. The official todo entry\n * (`conversation-todo-dock` in `dsh-client-ui-conversation`) registers\n * `{ id: 'todo', order: 0 }` with the default priority 0, so\n * `{ id: 'todo', priority: -1 }` wins the cell and the official panel never\n * renders while this plugin is mounted.\n *\n * This is the same mechanism `dsh-input-traffic` uses on the sibling `queue`\n * cell (`{ id: 'queue', priority: -1 }`), so it is a proven seam on this host\n * generation, not a guess.\n *\n * Binding to a built-in id is a real coupling: if upstream renames the cell,\n * the shadow silently stops winning and the official dock simply reappears\n * (fail-open — the data is still shown, just twice). `slots.inject` is used\n * rather than a bare `register` because the slot is declared by a built-in\n * entry whose lifetime we do not control: `inject` waits for the declaration\n * and re-installs the entry if the declaration is ever recreated.\n *\n * Renders nothing on purpose: the todo UI now lives in this plugin's sidebar\n * tab. Return a summary chip here instead to keep a one-line entry point in the\n * composer band.\n */\nimport type { ReactNode } from 'react'\nimport type { Context } from '@deepseek-ai/cordis'\n\n/** The built-in cell id of the official todo dock. */\nexport const TODO_CELL_ID = 'todo'\n\n/** Lower than the built-in's default 0 — the winner of the `todo` cell. */\nexport const TODO_SHADOW_PRIORITY = -1\n\n/** Structural view of the client slots service (avoids a second type graph). */\nexport interface SlotsLike {\n inject(key: string, callback: () => (() => void) | undefined): () => void\n register(options: Record<string, unknown>, component: (props: unknown) => ReactNode): () => void\n}\n\n/** The replacement entry: deliberately empty. */\nfunction TodoDockShadow(): ReactNode {\n return null\n}\n\n/**\n * Register the shadowing entry.\n *\n * The caller wraps this in `ctx.effect(...)` so the returned disposer rides the\n * plugin fiber: unloading the plugin restores the official dock.\n *\n * @param ctx - the client root context.\n * @returns the disposer, or undefined when the slots service is unreachable.\n */\nexport function registerTodoDockShadow(ctx: Context): (() => void) | undefined {\n const slots = ctx.get('slots') as SlotsLike | undefined\n if (slots === undefined || typeof slots.inject !== 'function') return undefined\n\n return slots.inject('conversation.input.dock', () =>\n slots.register(\n {\n name: 'conversation.input.dock',\n id: TODO_CELL_ID,\n order: 0,\n priority: TODO_SHADOW_PRIORITY,\n registrant: 'dsh-brief-sidebar',\n },\n TodoDockShadow,\n ),\n )\n}\n","/**\n * Browser half: the task board tab, plus the shadow that hides the official\n * composer-band todo panel.\n *\n * Registration order matters and is deliberate:\n *\n * 1. **Dictionaries first.** Every later registration can render translated\n * copy, and a dictionary that lands after the first paint would show raw\n * keys for a frame.\n * 2. **Soft-dependency check.** `inject` already waits for `betterSidebar`, so\n * the guard only fires in a composition that relaxes the inject list. It is\n * kept because it is what stops the plugin from hiding the official panel\n * when it has no board to replace it with.\n * 3. **Shadow the official dock.** Deliberately BEFORE the tab registration:\n * the panel is the only other carrier of the list, so the replacement has to\n * exist in the same activation.\n * 4. **Register the tab.**\n *\n * Every registration rides `ctx.effect(fn, label)` so its disposer is revoked on\n * fiber teardown (HMR / disable), which is also what makes the shadow\n * reversible: unload the plugin and the official dock renders again.\n *\n * Contract notes for the two surfaces touched here:\n * - better-sidebar tabs are hosted by DSH's native right Sidebar, so the tab\n * body receives `TabComponentProps` (`ctx` + `scope` + `visible`) and NOT the\n * framework's session-scoped slot props.\n * - `conversation.input.dock` is the DSH slot the official todo panel occupies;\n * see `brief/dock-shadow.tsx` for why a lower priority wins the cell.\n */\nimport { createElement } from 'react'\nimport type { Context } from '@deepseek-ai/cordis'\nimport type { BetterSidebarService, TabComponentProps } from 'dsh-better-sidebar/client/service'\nimport { SummaryTab } from './SummaryTab'\nimport { BriefIcon } from './BriefIcon'\nimport { NS, dictionaries } from './locales'\nimport { readTodos, unfinishedCount } from './brief/board'\nimport { registerTodoDockShadow } from './brief/dock-shadow'\nimport { TODOS_KEY } from './brief/use-todos'\nimport { projectionSnapshot } from './use-projection'\n\n/**\n * Tab type id. Package-prefixed so it cannot collide with a built-in type.\n * Unchanged since the 0.1.0 board tab: the summary tab REPLACES the board tab\n * in place, so an opened or pinned tab survives the upgrade.\n */\nexport const TAB_ID = 'dsh-brief-sidebar:board'\n\n/**\n * Position in the host's new-tab guide.\n *\n * Built-ins on this host generation sit at editor 10 / git 20 / subagent 30 /\n * sidechat 35 / terminal 40 / browser 50. 15 places a per-session task board\n * right after the editor, which is where a reader looks for session state.\n */\nexport const TAB_ORDER = 15\n\n/** Services required before `apply` runs. */\nexport const inject = ['betterSidebar', 'locale', 'slots']\n\n/**\n * Structural view of the DSH locale service — only the two members this plugin\n * calls. Declared locally on purpose: importing the real type would pull the\n * whole client type graph into a browser-only plugin for two signatures.\n */\ninterface LocaleLike {\n register(ns: string, dicts: Record<string, Record<string, string>>): () => void\n bind(ns: string): (key: string) => string\n}\n\n/**\n * An effect body must return a disposer (cordis rejects `undefined` with a\n * `TypeError`), so a path that could not register anything returns this no-op\n * instead of nothing.\n */\nconst NOOP = (): void => {}\n\n/**\n * Browser-face apply.\n *\n * @param ctx - the client root context.\n */\nexport function apply(ctx: Context): void {\n // 1. Plugin-owned dictionaries, in one registration.\n const locale: LocaleLike | undefined = ctx.get('locale')\n if (locale !== undefined) {\n ctx.effect(() => locale.register(NS, dictionaries), 'dsh-brief-sidebar: dictionaries')\n }\n\n /**\n * Translate one of our keys. `bind` returns a cached function that reads the\n * ACTIVE locale at call time, so a language switch needs no re-apply; an\n * unknown namespace or key falls back to the key itself rather than throwing\n * inside a render.\n */\n const t = (key: string): string => {\n if (locale === undefined) return key\n try {\n return locale.bind(NS)(key) || key\n } catch {\n return key\n }\n }\n\n // 2. Soft dependency: without better-sidebar there is no place to show the\n // list, so the plugin stays inert and the official panel keeps rendering.\n const bar: BetterSidebarService | undefined = ctx.get('betterSidebar')\n if (bar === undefined) return\n\n // 3. Hide the official composer-band panel (reversible on unload).\n ctx.effect(() => registerTodoDockShadow(ctx) ?? NOOP, 'dsh-brief-sidebar: dock shadow')\n\n // 4. The board.\n ctx.effect(\n () =>\n bar.registerTab({\n id: TAB_ID,\n title: () => t('tab.title'),\n description: () => t('tab.desc'),\n icon: (size: number) => BriefIcon(size),\n order: TAB_ORDER,\n // Shorthand for `dedupeKey: () => id`: reopening focuses the existing tab.\n single: true,\n /**\n * Unfinished count on the tab strip. Called on every tab-bar render, so\n * it reads one snapshot and allocates no subscription; a completed list\n * shows no badge at all.\n */\n badge: (badgeCtx, badgeScope) => {\n const todos = readTodos(projectionSnapshot(badgeCtx, badgeScope.sessionId, TODOS_KEY))\n if (todos === undefined) return null\n const pending = unfinishedCount(todos)\n return pending > 0 ? pending : null\n },\n component: (props: TabComponentProps) =>\n createElement(SummaryTab, {\n t,\n ctx: props.ctx,\n scope: props.scope,\n visible: props.visible,\n }),\n }),\n 'dsh-brief-sidebar: tab',\n )\n}\n"],"mappings":"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"}
|