dsh-report-ledger 0.1.0 → 0.2.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":["Fragment"],"sources":["../src/client/timeline-model.ts","../src/client/ReportsView.tsx","../src/client/locales.ts","../src/client/index.ts"],"sourcesContent":["/**\n * The timeline's data model: turning a payload into rows, and filtering them.\n *\n * Deliberately a plain module with no React and no DOM so the decisions a user\n * actually feels — which rows appear, what a search matches, how the status\n * chips count — are pinned by deterministic tests instead of being inferred from\n * a browser. The view imports this; it does not re-implement it.\n *\n * @module dsh-report-ledger/client/timeline-model\n */\n\nimport type { ReportFrontMatter, ReportStatus, TimelinePayload } from '../shared/wire.ts'\n\n/** One rendered timeline row: a session branching, or a report appearing. */\nexport type Row =\n | {\n readonly kind: 'session'\n readonly at: number\n readonly id: string\n readonly depth: number\n readonly title?: string\n readonly delegated: boolean\n readonly live: boolean\n }\n | {\n readonly kind: 'report'\n readonly at: number\n readonly depth: number\n readonly front: ReportFrontMatter\n }\n\n/** Which lifecycle states the status chips currently admit. */\nexport type StatusFilter = 'all' | ReportStatus\n\n/** How a report is placed on the time axis. */\nexport type ReportTimeBasis = 'created' | 'updated'\n\n/** Filters the view applies to the assembled rows. */\nexport interface RowFilter {\n /** Lifecycle filter; applies to report rows only. */\n readonly status: StatusFilter\n /** Free text; applies to every row. */\n readonly text: string\n}\n\n/**\n * Build the merged, time-ordered row list.\n *\n * Sessions are placed at their creation time and reports at theirs, so the list\n * reads as the collaboration actually unfolded: a session appears where it was\n * branched, a report where it was opened. Report depth comes from the session\n * that authored it, which is what keeps the indentation meaningful even when the\n * session row itself is filtered out.\n * @param payload - the endpoint payload.\n * @param basis - whether to place a report at its creation or its last activity.\n * @returns the rows, oldest first.\n */\nexport function buildRows(payload: TimelinePayload, basis: ReportTimeBasis = 'created'): Row[] {\n const depth = new Map<string, number>()\n const rows: Row[] = []\n for (const node of payload.sessions) {\n depth.set(node.id, node.depth)\n rows.push({\n kind: 'session',\n at: node.createdAt ?? 0,\n id: node.id,\n depth: node.depth,\n delegated: node.delegated,\n live: node.live,\n ...(node.title === undefined ? {} : { title: node.title }),\n })\n }\n for (const front of payload.reports) {\n rows.push({\n kind: 'report',\n at: basis === 'created' ? front.created : front.updated,\n depth: depth.get(front.from) ?? 0,\n front,\n })\n }\n rows.sort((left, right) => left.at - right.at\n || (left.kind === right.kind ? 0 : left.kind === 'session' ? -1 : 1))\n return rows\n}\n\n/** Every string a report row can be found by. */\nfunction reportHaystack(front: ReportFrontMatter): string {\n return [\n front.report,\n front.subject,\n front.from,\n front.fromName ?? '',\n ...front.to,\n ...front.cc,\n ...front.authors,\n front.task ?? '',\n ...front.artifacts,\n ].join('\\n').toLowerCase()\n}\n\n/** Every string a session row can be found by. */\nfunction sessionHaystack(row: Extract<Row, { kind: 'session' }>): string {\n return `${row.title ?? ''}\\n${row.id}`.toLowerCase()\n}\n\n/**\n * Whether one row survives the current filter.\n * @param row - the candidate row.\n * @param filter - the active filter.\n * @returns true when the row should be rendered.\n */\nexport function rowMatches(row: Row, filter: RowFilter): boolean {\n const needle = filter.text.trim().toLowerCase()\n if (row.kind === 'session') {\n return needle === '' || sessionHaystack(row).includes(needle)\n }\n if (filter.status !== 'all' && row.front.status !== filter.status) return false\n return needle === '' || reportHaystack(row.front).includes(needle)\n}\n\n/**\n * Apply a filter to the row list.\n * @param rows - the assembled rows.\n * @param filter - the active filter.\n * @returns the surviving rows, in their original order.\n */\nexport function filterRows(rows: readonly Row[], filter: RowFilter): Row[] {\n return rows.filter((row) => rowMatches(row, filter))\n}\n\n/** Counts per lifecycle state, plus the total. */\nexport interface StatusCounts {\n /** Every report. */\n readonly all: number\n /** Reports awaiting action or awareness. */\n readonly open: number\n /** Reports whose recipients have acknowledged. */\n readonly acked: number\n /** Reports the owners have concluded. */\n readonly closed: number\n}\n\n/**\n * Count reports by lifecycle state.\n *\n * Counted from the whole subtree payload rather than the filtered rows, so the\n * chips keep showing the full picture while one of them is applied — a filter\n * whose own label changed when you used it would make it impossible to see how\n * much you had excluded.\n * @param reports - every report in the payload.\n * @returns the counts.\n */\nexport function statusCounts(reports: readonly ReportFrontMatter[]): StatusCounts {\n const counts = { all: reports.length, open: 0, acked: 0, closed: 0 }\n for (const front of reports) {\n if (front.status === 'open') counts.open += 1\n else if (front.status === 'acked') counts.acked += 1\n else if (front.status === 'closed') counts.closed += 1\n }\n return counts\n}\n\n/**\n * Whether a filter would hide anything.\n * @param filter - the active filter.\n * @returns true when a status or text constraint is in force.\n */\nexport function isFiltering(filter: RowFilter): boolean {\n return filter.status !== 'all' || filter.text.trim() !== ''\n}\n\n/**\n * Count how many report rows are visible under a filter.\n * @param rows - every assembled row.\n * @param filter - the active filter.\n * @returns the number of visible report rows.\n */\nexport function visibleReports(rows: readonly Row[], filter: RowFilter): number {\n let count = 0\n for (const row of filterRows(rows, filter)) if (row.kind === 'report') count += 1\n return count\n}\n\n/**\n * The report ids present in one payload.\n *\n * A thread link can point outside the subtree — a report answering one opened by\n * an unrelated session, for instance — and the detail endpoint is ledger-scoped,\n * so such a report still opens. Knowing which ids are in the list is what lets\n * the view say so instead of silently showing a card that is not in the timeline.\n * @param payload - the endpoint payload.\n * @returns the ids of every report in the payload.\n */\nexport function reportIds(payload: TimelinePayload): ReadonlySet<string> {\n const ids = new Set<string>()\n for (const front of payload.reports) ids.add(front.report)\n return ids\n}\n\n/** Body length above which the detail panel shows only a head. */\nexport const BODY_DISPLAY_LIMIT = 4000\n\n/** How much of an over-long body is shown. */\nexport const BODY_HEAD_LIMIT = 1200\n\n/**\n * Trim a body for the detail panel.\n *\n * Mirrors what `report_read` already does for the model: the full text stays in\n * the ledger file, and both readers get a bounded view plus a pointer rather than\n * an unbounded paste. Keeping the human view and the model view bounded the same\n * way means neither can be surprised by what the other sees.\n * @param body - the raw body.\n * @param limit - length above which truncation applies.\n * @param head - how many characters to keep when truncating.\n * @returns the display text and whether it was cut.\n */\nexport function displayBody(\n body: string,\n limit: number = BODY_DISPLAY_LIMIT,\n head: number = BODY_HEAD_LIMIT,\n): { readonly text: string; readonly truncated: boolean } {\n if (body.length <= limit) return { text: body, truncated: false }\n return { text: body.slice(0, head), truncated: true }\n}\n","/**\n * The report timeline view — the `conversation.view` tab this plugin registers.\n *\n * It renders one top-to-bottom timeline of the collaboration: the session\n * subtree as it branched, interleaved with the reports those sessions produced\n * or were copied on. Rows carry only the digest, so a long-running tree stays\n * scannable; clicking a report opens its transfer path and body on demand.\n *\n * Data comes from this plugin's own read-only endpoint rather than a generated\n * remote: the host assembles the subtree and filters the ledger to the sessions\n * in it, which the browser cannot do on its own because a report's transfer path\n * is a host-side file, not session state.\n *\n * A blank session never reaches this component — the conversation slot is\n * omitted entirely in that state.\n *\n * @module dsh-report-ledger/client/ReportsView\n */\n\nimport { Fragment, useCallback, useEffect, useMemo, useState } from 'react'\nimport type { ReactElement } from 'react'\nimport type { ReportDetail, TimelinePayload } from '../shared/wire.ts'\nimport {\n buildRows,\n displayBody,\n filterRows,\n isFiltering,\n reportIds,\n statusCounts,\n visibleReports,\n type ReportTimeBasis,\n type Row,\n type StatusFilter,\n} from './timeline-model.ts'\nimport type { ReportLedgerKey } from './locales.ts'\n\n/** Translate one key, substituting `{name}` placeholders. */\nexport type Translate = (key: ReportLedgerKey, params?: Record<string, string | number>) => string\n\n/** Props the registration passes through, plus the locale seat. */\nexport interface ReportsViewProps {\n /** The session the view is bound to. */\n sessionId?: string\n /** Injected by the registration closure from the plugin's locale namespace. */\n t: Translate\n}\n\nconst TIMELINE_URL = '/api/report-ledger/timeline'\nconst REPORT_URL = '/api/report-ledger/report'\n\n/** Read one endpoint and unwrap its result envelope. */\nasync function getJson<T>(url: string): Promise<T> {\n const response = await fetch(url, { credentials: 'same-origin', headers: { accept: 'application/json' } })\n let body: unknown\n try {\n body = await response.json()\n } catch {\n throw new Error(`HTTP ${response.status}`)\n }\n const envelope = body as { ok?: boolean; code?: string; value?: T }\n if (envelope.ok !== true) throw new Error(envelope.code ?? `HTTP ${response.status}`)\n return envelope.value as T\n}\n\n/** Substitute `{name}` placeholders. */\nfunction fill(template: string, params: Record<string, string | number>): string {\n return template.replace(/\\{(\\w+)\\}/g, (whole, name: string) =>\n Object.prototype.hasOwnProperty.call(params, name) ? String(params[name]) : whole)\n}\n\n/** Format one timestamp for a timeline row. */\nfunction stamp(at: number | undefined): string {\n if (at === undefined || at <= 0) return ''\n return new Date(at).toLocaleString()\n}\n\n/** Colour a status chip from the theme's state tokens. */\nfunction statusStyle(status: string): Record<string, string> {\n if (status === 'acked') {\n return {\n color: 'var(--dsw-alias-state-success-label, #1a7f37)',\n background: 'var(--dsw-alias-state-success-bg, rgba(26,127,55,0.10))',\n }\n }\n if (status === 'closed') {\n return {\n color: 'var(--dsw-alias-label-tertiary, #888)',\n background: 'var(--dsw-alias-bg-layer-2, rgba(0,0,0,0.04))',\n }\n }\n return {\n color: 'var(--dsw-alias-state-warn-label, #9a6700)',\n background: 'var(--dsw-alias-state-warn-bg, rgba(154,103,0,0.10))',\n }\n}\n\n/** Storage key for the remembered status filter. */\nconst FILTER_KEY = 'dsh.reportLedger.filter.v1'\n\n/** Read the remembered status filter, tolerating absent or damaged storage. */\nfunction rememberedStatus(): StatusFilter {\n try {\n const raw = globalThis.localStorage?.getItem(FILTER_KEY)\n if (typeof raw === 'string') {\n const parsed = JSON.parse(raw) as { status?: unknown }\n if (parsed.status === 'open' || parsed.status === 'acked' || parsed.status === 'closed') return parsed.status\n }\n } catch {\n // A restricted or full storage just means the filter is not remembered.\n }\n return 'all'\n}\n\n/**\n * Render the report timeline.\n * @param props - the session binding and the locale seat.\n * @returns the tab content.\n */\nexport function ReportsView(props: ReportsViewProps): ReactElement {\n const { sessionId, t } = props\n const [payload, setPayload] = useState<TimelinePayload | undefined>(undefined)\n const [error, setError] = useState<string | undefined>(undefined)\n const [nonce, setNonce] = useState(0)\n const [openReport, setOpenReport] = useState<string | undefined>(undefined)\n const [detail, setDetail] = useState<ReportDetail | undefined>(undefined)\n const [detailError, setDetailError] = useState<string | undefined>(undefined)\n const [status, setStatus] = useState<StatusFilter>(rememberedStatus)\n const [text, setText] = useState('')\n const [basis, setBasis] = useState<ReportTimeBasis>('created')\n\n useEffect(() => {\n try {\n globalThis.localStorage?.setItem(FILTER_KEY, JSON.stringify({ status }))\n } catch {\n // Remembering the filter is a convenience, never a requirement.\n }\n }, [status])\n\n useEffect(() => {\n if (sessionId === undefined || sessionId === '') {\n setError('no-session')\n return\n }\n let cancelled = false\n setError(undefined)\n getJson<TimelinePayload>(`${TIMELINE_URL}?root=${encodeURIComponent(sessionId)}`)\n .then((value) => { if (!cancelled) setPayload(value) })\n .catch((cause: unknown) => { if (!cancelled) setError(cause instanceof Error ? cause.message : String(cause)) })\n return () => { cancelled = true }\n }, [sessionId, nonce])\n\n useEffect(() => {\n if (openReport === undefined) return\n let cancelled = false\n setDetail(undefined)\n setDetailError(undefined)\n getJson<ReportDetail>(`${REPORT_URL}?id=${encodeURIComponent(openReport)}`)\n .then((value) => { if (!cancelled) setDetail(value) })\n .catch((cause: unknown) => { if (!cancelled) setDetailError(cause instanceof Error ? cause.message : String(cause)) })\n return () => { cancelled = true }\n }, [openReport, nonce])\n\n const rows = useMemo<Row[]>(() => (payload === undefined ? [] : buildRows(payload, basis)), [payload, basis])\n const counts = useMemo(() => statusCounts(payload?.reports ?? []), [payload])\n const inList = useMemo(() => (payload === undefined ? new Set<string>() : reportIds(payload)), [payload])\n const shownBody = useMemo(() => displayBody(detail?.body ?? ''), [detail])\n const filter = useMemo(() => ({ status, text }), [status, text])\n const visible = useMemo<Row[]>(() => {\n const kept = filterRows(rows, filter)\n // The detail panel is anchored to a report row, so the open report must HAVE a\n // row. Two cases would otherwise leave the panel with nowhere to render: a\n // thread link pointing at a report outside this tree's payload, and any filter\n // applied while a report is open. Both are real user paths, and in the first\n // the panel is the only place the \"not in this tree\" notice can appear — so the\n // row is synthesized from the fetched detail instead of the panel being hoisted\n // out of the list.\n if (openReport === undefined || detail === undefined) return kept\n if (kept.some((row) => row.kind === 'report' && row.front.report === openReport)) return kept\n return [...kept, { kind: 'report', at: detail.front.created, depth: 0, front: detail.front }]\n }, [rows, filter, openReport, detail])\n // Reports surviving the FILTER, as opposed to `visible`, which also carries a\n // synthesized row for the open report. Keeping the two apart is what lets the\n // panel say which of the two reasons applies: outside this tree, or hidden by\n // the current filter. Claiming \"another line\" for a filtered report would be\n // simply wrong.\n const filteredReportIds = useMemo(() => {\n const ids = new Set<string>()\n for (const row of filterRows(rows, filter)) if (row.kind === 'report') ids.add(row.front.report)\n return ids\n }, [rows, filter])\n const filtering = isFiltering(filter)\n // Counted from the unfiltered row set so a synthesized out-of-tree row cannot\n // inflate the summary.\n const visibleReportCount = useMemo(() => visibleReports(rows, filter), [rows, filter])\n\n const refresh = useCallback(() => { setNonce((value) => value + 1) }, [])\n\n const shell: Record<string, string | number> = {\n padding: '16px 20px',\n overflowY: 'auto',\n height: '100%',\n font: 'var(--dsw-font-xs-13, 13px/1.6 system-ui, sans-serif)',\n color: 'var(--dsw-alias-label-primary, inherit)',\n }\n const head: Record<string, string | number> = {\n display: 'flex',\n alignItems: 'baseline',\n gap: '12px',\n marginBottom: '12px',\n }\n const summary: Record<string, string | number> = {\n color: 'var(--dsw-alias-label-tertiary, #888)',\n fontSize: 'var(--dsw-font-xxs-12, 12)',\n flex: '1 1 auto',\n }\n const button: Record<string, string | number> = {\n font: 'inherit',\n color: 'var(--dsw-alias-label-secondary, inherit)',\n background: 'var(--dsw-alias-bg-layer-2, transparent)',\n border: '1px solid var(--dsw-alias-border-l2, rgba(0,0,0,0.12))',\n borderRadius: '6px',\n padding: '3px 10px',\n cursor: 'pointer',\n }\n const chip = (extra: Record<string, string>): Record<string, string | number> => ({\n display: 'inline-block',\n padding: '0 6px',\n borderRadius: '999px',\n fontSize: 'var(--dsw-font-xxxs-11, 11)',\n lineHeight: '17px',\n marginRight: '6px',\n ...extra,\n })\n const tools: Record<string, string | number> = {\n display: 'flex',\n alignItems: 'center',\n flexWrap: 'wrap',\n gap: '6px',\n marginBottom: '10px',\n }\n const time: Record<string, string | number> = {\n color: 'var(--dsw-alias-label-caption, #999)',\n fontSize: 'var(--dsw-font-xxxs-11, 11)',\n whiteSpace: 'nowrap',\n }\n\n if (error !== undefined) {\n return (\n <div style={shell}>\n <div style={{ color: 'var(--dsw-alias-state-error-label, #b00)' }}>{t('view.error', { code: error })}</div>\n <div style={{ marginTop: '8px' }}>\n <button type=\"button\" style={button} onClick={refresh}>{t('view.retry')}</button>\n </div>\n </div>\n )\n }\n\n if (payload === undefined) {\n return <div style={shell}>{t('view.loading')}</div>\n }\n\n const statusLabel: Record<StatusFilter, ReportLedgerKey> = {\n all: 'filter.all',\n open: 'filter.open',\n acked: 'filter.acked',\n closed: 'filter.closed',\n }\n const statusCount: Record<StatusFilter, number> = {\n all: counts.all,\n open: counts.open,\n acked: counts.acked,\n closed: counts.closed,\n }\n const chips: StatusFilter[] = ['all', 'open', 'acked', 'closed']\n const searchInput: Record<string, string | number> = {\n flex: '1 1 200px',\n minWidth: '140px',\n padding: '3px 8px',\n font: 'inherit',\n color: 'var(--dsw-alias-label-primary, inherit)',\n background: 'var(--dsw-alias-bg-base, rgba(0,0,0,0.02))',\n border: '1px solid var(--dsw-alias-border-l2, rgba(0,0,0,0.12))',\n borderRadius: '6px',\n }\n\n return (\n <div style={shell}>\n <div style={head}>\n <strong>{t('view.tab')}</strong>\n <span style={summary}>\n {filtering\n ? t('filter.showing', { visible: visibleReportCount, total: counts.all })\n : t('view.summary', { sessions: payload.sessions.length, reports: payload.reports.length })}\n </span>\n <button type=\"button\" style={button} onClick={refresh}>{t('view.refresh')}</button>\n </div>\n\n <div style={tools} title={t('filter.hint')}>\n {chips.map((candidate) => {\n const active = status === candidate\n return (\n <button\n key={candidate}\n type=\"button\"\n aria-pressed={active}\n onClick={() => { setStatus(candidate) }}\n style={{\n ...button,\n ...(active\n ? {\n color: 'var(--dsw-alias-label-primary, inherit)',\n background: 'var(--dsw-alias-interactive-bg-active, rgba(0,0,0,0.08))',\n borderColor: 'var(--dsw-alias-border-l3, rgba(0,0,0,0.20))',\n }\n : {}),\n }}\n >\n {`${t(statusLabel[candidate])} ${statusCount[candidate]}`}\n </button>\n )\n })}\n <input\n type=\"search\"\n value={text}\n placeholder={t('filter.search')}\n aria-label={t('filter.search')}\n onChange={(event) => { setText(event.target.value) }}\n style={searchInput}\n />\n {filtering ? (\n <button\n type=\"button\"\n style={button}\n onClick={() => { setStatus('all'); setText('') }}\n >\n {t('filter.clear')}\n </button>\n ) : null}\n <button\n type=\"button\"\n style={button}\n title={t('basis.hint')}\n onClick={() => { setBasis(basis === 'created' ? 'updated' : 'created') }}\n >\n {t(basis === 'created' ? 'basis.created' : 'basis.updated')}\n </button>\n </div>\n\n {rows.length === 0 ? <div style={{ color: 'var(--dsw-alias-label-tertiary, #888)' }}>{t('view.empty')}</div> : null}\n {rows.length > 0 && visible.length === 0 ? (\n <div style={{ color: 'var(--dsw-alias-label-tertiary, #888)' }}>{t('filter.none')}</div>\n ) : null}\n\n {visible.map((row, index) => {\n const pad = 8 + row.depth * 18\n if (row.kind === 'session') {\n return (\n <div key={`s-${row.id}-${index}`} style={{ display: 'flex', gap: '8px', padding: `4px 0 4px ${pad}px`, alignItems: 'baseline' }}>\n <span style={{ color: 'var(--dsw-alias-label-tertiary, #999)' }}>●</span>\n <span style={{ flex: '1 1 auto' }}>\n {row.delegated ? <span style={chip({ color: 'var(--dsw-alias-state-business-label, #0969da)', background: 'var(--dsw-alias-state-business-bg, rgba(9,105,218,0.10))' })}>{t('view.delegated')}</span> : null}\n {row.live ? <span style={chip({ color: 'var(--dsw-alias-state-success-label, #1a7f37)', background: 'var(--dsw-alias-state-success-bg, rgba(26,127,55,0.10))' })}>{t('view.live')}</span> : null}\n {row.title ?? row.id}\n </span>\n <span style={time}>{stamp(row.at)}</span>\n </div>\n )\n }\n const front = row.front\n const expanded = openReport === front.report\n return (\n <div key={`r-${front.report}`} style={{ paddingLeft: `${pad}px` }}>\n <div\n role=\"button\"\n tabIndex={0}\n onClick={() => { setOpenReport(expanded ? undefined : front.report) }}\n onKeyDown={(event) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault()\n setOpenReport(expanded ? undefined : front.report)\n }\n }}\n style={{\n display: 'flex',\n gap: '8px',\n alignItems: 'baseline',\n padding: '5px 8px',\n margin: '2px 0',\n borderRadius: '6px',\n cursor: 'pointer',\n background: 'var(--dsw-alias-bg-layer-1, rgba(0,0,0,0.02))',\n border: '1px solid var(--dsw-alias-border-l1, rgba(0,0,0,0.06))',\n }}\n >\n <span style={{ color: 'var(--dsw-alias-label-tertiary, #999)' }}>{expanded ? '▾' : '▸'}</span>\n <span style={{ flex: '1 1 auto' }}>\n <span style={chip(statusStyle(front.status))}>{front.status}</span>\n <strong>{front.report}</strong> {front.subject}\n {/* The task chip is a filter shortcut, not decoration: clicking it\n reuses the search box, so grouping by collaboration needs no\n state of its own. It stops the click from also toggling the\n detail panel. */}\n {front.task === undefined ? null : (\n <button\n type=\"button\"\n title={fill(t('row.filterByTask'), { task: front.task })}\n onClick={(event) => { event.stopPropagation(); setText(front.task as string) }}\n style={{\n ...chip({\n color: 'var(--dsw-alias-state-business-label, #0969da)',\n background: 'var(--dsw-alias-state-business-bg, rgba(9,105,218,0.10))',\n }),\n border: 0,\n cursor: 'pointer',\n font: 'inherit',\n }}\n >\n {front.task}\n </button>\n )}\n <div style={{ ...time, marginTop: '2px' }}>\n {`from=${front.from}`}\n {front.to.length > 0 ? ` → ${front.to.join(', ')}` : ''}\n {front.cc.length > 0 ? ` cc ${front.cc.join(', ')}` : ''}\n {` · ${fill(t('row.hops'), { count: front.hops })}`}\n {front.authors.length > 1 ? ` · ${fill(t('row.authors'), { count: front.authors.length })}` : ''}\n {front.last === undefined ? '' : ` · last=${front.last.action}`}\n </div>\n </span>\n <span style={time}>{stamp(front.updated)}</span>\n </div>\n\n {expanded ? (\n <div style={{\n margin: '4px 0 10px 24px',\n padding: '10px 12px',\n borderRadius: '6px',\n background: 'var(--dsw-alias-bg-layer-2, rgba(0,0,0,0.03))',\n border: '1px solid var(--dsw-alias-border-l1, rgba(0,0,0,0.06))',\n }}>\n {detailError !== undefined ? <div style={{ color: 'var(--dsw-alias-state-error-label, #b00)' }}>{t('view.error', { code: detailError })}</div> : null}\n {detail === undefined && detailError === undefined ? <div style={time}>{t('view.detail.loading')}</div> : null}\n {detail === undefined ? null : (\n <>\n <div style={{ marginBottom: '6px' }}><strong>{t('view.detail.route')}</strong></div>\n {detail.hops.length === 0 ? <div style={time}>{t('view.detail.routeEmpty')}</div> : (\n <div style={{ display: 'grid', gridTemplateColumns: 'auto auto auto 1fr', gap: '2px 10px', marginBottom: '8px' }}>\n {detail.hops.map((hop, hopIndex) => (\n <Fragment key={`h-${hopIndex}`}>\n <span style={time}>{stamp(hop.at)}</span>\n <span><strong>{hop.action}</strong></span>\n <span style={time}>{hop.actor}</span>\n <span style={time}>{hop.to.length === 0 ? (hop.note ?? '') : `→ ${hop.to.join(', ')}${hop.note === undefined ? '' : ` (${hop.note})`}`}</span>\n </Fragment>\n ))}\n </div>\n )}\n {detail.pending.length === 0 ? null : (\n <div style={{ ...time, color: 'var(--dsw-alias-state-warn-label, #9a6700)' }}>\n {fill(t('view.detail.pending'), { targets: detail.pending.join(', ') })}\n </div>\n )}\n\n {(() => {\n const parent = detail.front.parent\n const children = detail.front.children\n if (parent === undefined && children.length === 0) return null\n const link = (target: string): ReactElement => (\n <button\n key={target}\n type=\"button\"\n title={fill(t('detail.openLinked'), { report: target })}\n onClick={() => { setOpenReport(target) }}\n style={{ ...button, marginRight: '6px', font: 'var(--dsw-font-xxxs-11, 11px/1.4 ui-monospace, monospace)' }}\n >\n {target}\n </button>\n )\n return (\n <div style={{ marginBottom: '8px' }}>\n <strong>{t('detail.thread')}</strong>\n {parent === undefined ? null : (\n <div style={{ marginTop: '2px' }}>\n <span style={time}>{`${t('detail.parent')}: `}</span>{link(parent)}\n </div>\n )}\n {children.length === 0 ? null : (\n <div style={{ marginTop: '2px' }}>\n <span style={time}>{`${t('detail.children')}: `}</span>{children.map(link)}\n </div>\n )}\n </div>\n )\n })()}\n\n {/* Kept OUT of the thread block above: that block returns early\n when a report has no thread links, which is the common case\n for exactly the reports this notice is about. */}\n {inList.has(detail.front.report) ? null : (\n <div style={{ ...time, marginBottom: '8px' }}>\n {fill(t('detail.outside'), { report: detail.front.report })}\n {' '}\n <button type=\"button\" style={button} onClick={() => { setOpenReport(undefined) }}>\n {t('detail.backToList')}\n </button>\n </div>\n )}\n {inList.has(detail.front.report) && !filteredReportIds.has(detail.front.report) ? (\n <div style={{ ...time, marginBottom: '8px' }}>\n {fill(t('detail.filteredOut'), { report: detail.front.report })}\n </div>\n ) : null}\n\n <div style={{ margin: '8px 0 4px' }}><strong>{t('view.detail.body')}</strong></div>\n <pre style={{\n margin: 0,\n padding: '8px 10px',\n maxHeight: '320px',\n overflow: 'auto',\n whiteSpace: 'pre-wrap',\n wordBreak: 'break-word',\n background: 'var(--dsw-alias-bg-base, rgba(0,0,0,0.02))',\n border: '1px solid var(--dsw-alias-border-l1, rgba(0,0,0,0.06))',\n borderRadius: '4px',\n font: 'var(--dsw-font-xs-13, 13px/1.5 ui-monospace, monospace)',\n }}>{shownBody.text.trim()}</pre>\n {shownBody.truncated ? (\n <div style={{ ...time, marginTop: '4px' }}>\n {fill(t('detail.bodyTruncated'), { chars: shownBody.text.trim().length })}\n </div>\n ) : null}\n <div style={{ ...time, marginTop: '6px', wordBreak: 'break-all' }}>{t('view.detail.path')}: {detail.path}</div>\n </>\n )}\n </div>\n ) : null}\n </div>\n )\n })}\n </div>\n )\n}\n","/**\n * Dictionary for the report timeline tab.\n *\n * Both shipped locales are required by the client locale service, and the key\n * union drives the typed `t()` the view uses, so a missing translation is a\n * type error rather than an empty label.\n *\n * @module dsh-report-ledger/client/locales\n */\n\n/** Chinese dictionary — the source of truth for the key set. */\nexport const zh = {\n 'view.tab': '汇报',\n 'view.loading': '正在读取账本…',\n 'view.summary': '{sessions} 个会话 · {reports} 份汇报',\n 'view.empty': '这个会话树下还没有汇报。代理用 report_author 开一份汇报后,这里会出现它的缩略卡。',\n 'view.refresh': '刷新',\n 'view.retry': '重试',\n 'view.error': '读取失败:{code}',\n 'view.root': '本会话',\n 'view.live': '在线',\n 'view.delegated': '子代理',\n 'view.detail.loading': '正在读取传递路径…',\n 'view.detail.route': '传递路径',\n 'view.detail.routeEmpty': '(还没有记录跳)',\n 'view.detail.pending': '仍在等待送达:{targets}',\n 'view.detail.body': '正文',\n 'view.detail.path': '账本文件',\n 'row.hops': '{count} 跳',\n 'row.authors': '{count} 位共写',\n 'row.filterByTask': '只看任务「{task}」',\n 'filter.all': '全部',\n 'filter.open': '进行中',\n 'filter.acked': '已回执',\n 'filter.closed': '已结案',\n 'filter.search': '搜索主题、编号、会话或收发…',\n 'filter.clear': '清除',\n 'filter.showing': '显示 {visible}/{total} 份汇报',\n 'filter.none': '没有符合当前筛选的汇报。',\n 'filter.hint': '状态筛选只作用于汇报;搜索会同时匹配会话标题与编号。',\n 'basis.created': '按创建',\n 'basis.updated': '按最近活动',\n 'basis.hint': '汇报按创建时间还是按最后一次活动排入时间线;会话始终按创建时间。',\n 'detail.thread': '线程',\n 'detail.parent': '上溯',\n 'detail.children': '下递',\n 'detail.openLinked': '打开 {report}',\n 'detail.outside': '{report} 不在当前会话树的列表里(它可能属于另一条线);正文与路径仍可读取。',\n 'detail.filteredOut': '{report} 在当前筛选下被隐藏了(用「清除」恢复)。',\n 'detail.backToList': '回到列表',\n 'detail.bodyTruncated': '正文过长,此处只显示开头 {chars} 字;完整内容见账本文件。',\n} as const\n\n/** English dictionary, checked against the Chinese key set. */\nexport const en: Record<keyof typeof zh, string> = {\n 'view.tab': 'Reports',\n 'view.loading': 'Reading the ledger…',\n 'view.summary': '{sessions} sessions · {reports} reports',\n 'view.empty': 'No reports in this session tree yet. Once an agent opens one with report_author, its digest card appears here.',\n 'view.refresh': 'Refresh',\n 'view.retry': 'Retry',\n 'view.error': 'Read failed: {code}',\n 'view.root': 'this session',\n 'view.live': 'live',\n 'view.delegated': 'subagent',\n 'view.detail.loading': 'Reading the transfer path…',\n 'view.detail.route': 'Transfer path',\n 'view.detail.routeEmpty': '(no hops recorded yet)',\n 'view.detail.pending': 'Still owed to: {targets}',\n 'view.detail.body': 'Body',\n 'view.detail.path': 'Ledger file',\n 'row.hops': '{count} hops',\n 'row.authors': '{count} authors',\n 'row.filterByTask': 'Show only task \"{task}\"',\n 'filter.all': 'All',\n 'filter.open': 'Open',\n 'filter.acked': 'Acked',\n 'filter.closed': 'Closed',\n 'filter.search': 'Search subject, id, session, or people…',\n 'filter.clear': 'Clear',\n 'filter.showing': 'Showing {visible}/{total} reports',\n 'filter.none': 'No reports match the current filter.',\n 'filter.hint': 'The status filter narrows reports only; search matches every row, including session titles and ids.',\n 'basis.created': 'By creation',\n 'basis.updated': 'By activity',\n 'basis.hint': 'Whether reports are placed at creation or at their latest activity; sessions always keep their creation time.',\n 'detail.thread': 'Thread',\n 'detail.parent': 'Answers',\n 'detail.children': 'Answered by',\n 'detail.openLinked': 'Open {report}',\n 'detail.outside': '{report} is not in this session tree\\'s list (it may belong to another line); its body and path are still readable.',\n 'detail.filteredOut': '{report} is hidden by the current filter (use Clear to restore it).',\n 'detail.backToList': 'Back to the list',\n 'detail.bodyTruncated': 'Body is long, so only the first {chars} characters are shown here; the ledger file has the whole thing.',\n}\n\n/** Every key the view may translate. */\nexport type ReportLedgerKey = keyof typeof zh\n","/**\n * Browser half of dsh-report-ledger — runs inside the dsh web GUI.\n *\n * The shell serves the built `lib/client.js` from /plugins/dsh-report-ledger/client.js\n * and evaluates it as a closure-factory artifact, so this module's `require` is\n * answered by the loader's frozen module table (react, cordis, the slot\n * services) and every other dependency is inlined by the build.\n *\n * It contributes exactly one thing: a `conversation.view` entry, which the\n * conversation shell projects as a tab beside the shipped Chat and Trajectory\n * views. A fresh id is used rather than an existing one, so nothing shipped is\n * replaced.\n *\n * @module dsh-report-ledger/client\n */\n\nimport { createElement } from 'react'\nimport { ReportsView, type Translate } from './ReportsView.tsx'\nimport { en, zh } from './locales.ts'\n\n/** Locale namespace this plugin owns. */\nconst NS = 'report-ledger'\n\n/** Tab key. Distinct from the shipped `chat` / `trajectory` cells. */\nconst VIEW_ID = 'report-ledger'\n\n/** Position after the shipped views (`chat` 0, `trajectory` 10). */\nconst VIEW_ORDER = 20\n\n/** Minimal structural face of the client slot service. */\ninterface SlotsService {\n inject(key: string, callback: () => () => void): () => void\n register(options: Record<string, unknown>, component: (props: never) => unknown): () => void\n}\n\n/** Minimal structural face of the client locale service. */\ninterface LocaleService {\n register(ns: string, dicts: { zh: unknown; en: unknown }): () => void\n bind(ns: string): Translate\n}\n\n/**\n * Minimal structural face of the client plugin context.\n *\n * Deliberately local rather than imported: the client runtime's published types\n * live in packages seeded into the shell bundle, which are not part of a\n * third-party plugin's resolvable dependency set (and one of them is not even\n * installed here). The shape asserted is exactly what the registration below\n * uses, and the host-side declarations remain the source of truth for the wire.\n */\ninterface ClientContext {\n slots: SlotsService\n locale: LocaleService\n effect(callback: () => (() => void) | void, label?: string): () => void\n get(name: string): unknown\n}\n\n/** The slot and locale services must be mounted before this half can contribute. */\nexport const inject = ['slots', 'locale']\n\n/**\n * Apply the browser half.\n * @param ctx - the client plugin context (`slots`, `locale` injected).\n */\nexport function apply(ctx: ClientContext): void {\n ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'report-ledger: dictionaries')\n\n const t = ctx.locale.bind(NS)\n\n ctx.slots.inject('conversation.view', () => ctx.slots.register(\n {\n name: 'conversation.view',\n id: VIEW_ID,\n order: VIEW_ORDER,\n // A thunk is re-read on every projection, so the tab label follows the\n // active locale without re-registering.\n label: () => t('view.tab'),\n locale: NS,\n },\n (props: Record<string, unknown>) => createElement(ReportsView, { ...props, t } as never) as never,\n ))\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"client.js","names":["Button","Tooltip","StateDot","Button","IconRefreshOutlineRegular","Pill","Input","IconChevronDownOutlineRegular","IconChevronRightOutlineRegular","Tag","Fragment","MarkdownText","IconCheckOutlineRegular","IconCopyOutlineRegular"],"sources":["../src/client/timeline-model.ts","../src/client/cardgraph-model.ts","../src/client/cardgraph-painter.ts","../src/client/locales.ts","../src/client/CardgraphView.tsx","../src/client/styles.ts","../src/client/ReportsView.tsx","../src/client/index.ts"],"sourcesContent":["/**\n * The timeline's data model: turning a payload into rows, and filtering them.\n *\n * Deliberately a plain module with no React and no DOM so the decisions a user\n * actually feels — which rows appear, what a search matches, how the status\n * chips count — are pinned by deterministic tests instead of being inferred from\n * a browser. The view imports this; it does not re-implement it.\n *\n * @module dsh-report-ledger/client/timeline-model\n */\n\nimport type { LeaseState, ReportFrontMatter, ReportStatus, ReportWorktree, TimelinePayload, WorktreeLane } from '../shared/wire.ts'\n\n/** One rendered timeline row: a session branching, or a report appearing. */\nexport type Row =\n | {\n readonly kind: 'session'\n readonly at: number\n readonly id: string\n readonly depth: number\n readonly title?: string\n readonly delegated: boolean\n readonly live: boolean\n }\n | {\n readonly kind: 'report'\n readonly at: number\n readonly depth: number\n readonly front: ReportFrontMatter\n }\n\n/** Which lifecycle states the status chips currently admit. */\nexport type StatusFilter = 'all' | ReportStatus\n\n/** How a report is placed on the time axis. */\nexport type ReportTimeBasis = 'created' | 'updated'\n\n/** Filters the view applies to the assembled rows. */\nexport interface RowFilter {\n /** Lifecycle filter; applies to report rows only. */\n readonly status: StatusFilter\n /** Free text; applies to every row. */\n readonly text: string\n}\n\n/**\n * The timeline's ordering: by time, and at the same instant a session before a\n * report.\n *\n * Extracted so that INSERTING a row uses the same rule as SORTING rows — two\n * copies would drift, and the synthesized row would land somewhere the sort\n * disagrees with.\n * @param left - one row.\n * @param right - the other row.\n * @returns the comparison result.\n */\nfunction byTime(left: Row, right: Row): number {\n return left.at - right.at\n || (left.kind === right.kind ? 0 : left.kind === 'session' ? -1 : 1)\n}\n\n/**\n * Build the merged, time-ordered row list.\n *\n * Sessions are placed at their creation time and reports at theirs, so the list\n * reads as the collaboration actually unfolded: a session appears where it was\n * branched, a report where it was opened. Report depth comes from the session\n * that authored it, which is what keeps the indentation meaningful even when the\n * session row itself is filtered out.\n * @param payload - the endpoint payload.\n * @param basis - whether to place a report at its creation or its last activity.\n * @returns the rows, oldest first.\n */\nexport function buildRows(payload: TimelinePayload, basis: ReportTimeBasis = 'created'): Row[] {\n const depth = new Map<string, number>()\n const rows: Row[] = []\n for (const node of payload.sessions) {\n depth.set(node.id, node.depth)\n rows.push({\n kind: 'session',\n at: node.createdAt ?? 0,\n id: node.id,\n depth: node.depth,\n delegated: node.delegated,\n live: node.live,\n ...(node.title === undefined ? {} : { title: node.title }),\n })\n }\n for (const front of payload.reports) {\n rows.push({\n kind: 'report',\n at: basis === 'created' ? front.created : front.updated,\n depth: depth.get(front.from) ?? 0,\n front,\n })\n }\n rows.sort(byTime)\n return rows\n}\n\n/** How many characters of a session id a dense row shows. */\nexport const SHORT_ID_LENGTH = 8\n\n/**\n * Shorten a session id so a digest row can be read.\n *\n * A session id is 36 characters and a report's recipients are a list of them, so\n * an unabbreviated meta line is mostly uuid and carries almost no information a\n * human can use. Eight characters of the leading segment stay unique within one\n * collaboration and, unlike a session title, are bounded: titles are the first\n * prompt of a session (\"You are doing READ-ONLY research\"), so swapping ids for\n * titles trades one unreadable line for another.\n *\n * This is a DENSE-ROW display only. The detail panel keeps full ids, because\n * that is the view somebody copies an id out of, and the tooltip on every\n * shortened id carries the full value.\n * @param id - the session id.\n * @param length - how many characters to keep.\n * @returns the shortened id.\n */\nexport function shortId(id: string, length: number = SHORT_ID_LENGTH): string {\n const bare = id.startsWith('session-') ? id.slice('session-'.length) : id\n return bare.length <= length ? bare : bare.slice(0, length)\n}\n\n/**\n * Display titles for the sessions in one payload, keyed by session id.\n *\n * Titles are resolved only for the subtree, so an id from another tree simply\n * has none — which is why every caller must fall back to the shortened id\n * rather than assuming a name exists.\n * @param payload - the endpoint payload.\n * @returns the titles that were resolved.\n */\nexport function sessionTitles(payload: TimelinePayload): Map<string, string> {\n const titles = new Map<string, string>()\n for (const node of payload.sessions) {\n if (typeof node.title === 'string' && node.title !== '') titles.set(node.id, node.title)\n }\n return titles\n}\n\n/**\n * Make sure the open report has a row, and put it where it belongs.\n *\n * The detail panel is anchored to a report row, so the open report must HAVE\n * one. Two real user paths would otherwise leave the panel with nowhere to\n * render: a thread link pointing at a report outside this tree's payload, and\n * any filter applied while a report is open. In the first case the panel is also\n * the only place the \"not in this tree\" notice can appear, so the panel is not\n * hoisted out of the list — a row is synthesized instead.\n *\n * The synthesized row is INSERTED at its own place on the time axis rather than\n * appended. Appending made a report created at 11:12 appear below one created at\n * 14:30, which reads as a broken sort at exactly the moment the user is trying\n * to understand why something is missing.\n * @param kept - the rows that survived the filter.\n * @param front - the digest of the open report.\n * @param basis - the active time basis, so the row moves with the rest.\n * @returns the rows, with the open report present exactly once.\n */\nexport function withSynthesizedRow(\n kept: readonly Row[],\n front: ReportFrontMatter,\n basis: ReportTimeBasis = 'created',\n): Row[] {\n if (kept.some((row) => row.kind === 'report' && row.front.report === front.report)) return [...kept]\n const row: Row = { kind: 'report', at: basis === 'created' ? front.created : front.updated, depth: 0, front }\n const index = kept.findIndex((candidate) => byTime(row, candidate) < 0)\n if (index < 0) return [...kept, row]\n return [...kept.slice(0, index), row, ...kept.slice(index)]\n}\n\n/** Every string a report row can be found by. */\nfunction reportHaystack(front: ReportFrontMatter): string {\n return [\n front.report,\n front.subject,\n front.from,\n front.fromName ?? '',\n ...front.to,\n ...front.cc,\n ...front.authors,\n front.task ?? '',\n ...front.artifacts,\n // The bound checkout is searchable for the same reason the task label is:\n // the chip that names it is a filter shortcut, so the text it sets has to be\n // text the search actually looks at.\n front.worktree?.branch ?? '',\n front.worktree?.path ?? '',\n ].join('\\n').toLowerCase()\n}\n\n/** Every string a session row can be found by. */\nfunction sessionHaystack(row: Extract<Row, { kind: 'session' }>): string {\n return `${row.title ?? ''}\\n${row.id}`.toLowerCase()\n}\n\n/**\n * Whether one row survives the current filter.\n * @param row - the candidate row.\n * @param filter - the active filter.\n * @returns true when the row should be rendered.\n */\nexport function rowMatches(row: Row, filter: RowFilter): boolean {\n const needle = filter.text.trim().toLowerCase()\n if (row.kind === 'session') {\n return needle === '' || sessionHaystack(row).includes(needle)\n }\n if (filter.status !== 'all' && row.front.status !== filter.status) return false\n return needle === '' || reportHaystack(row.front).includes(needle)\n}\n\n/**\n * Apply a filter to the row list.\n * @param rows - the assembled rows.\n * @param filter - the active filter.\n * @returns the surviving rows, in their original order.\n */\nexport function filterRows(rows: readonly Row[], filter: RowFilter): Row[] {\n return rows.filter((row) => rowMatches(row, filter))\n}\n\n/** Counts per lifecycle state, plus the total. */\nexport interface StatusCounts {\n /** Every report. */\n readonly all: number\n /** Reports awaiting action or awareness. */\n readonly open: number\n /** Reports whose recipients have acknowledged. */\n readonly acked: number\n /** Reports the owners have concluded. */\n readonly closed: number\n}\n\n/**\n * Count reports by lifecycle state.\n *\n * Counted from the whole subtree payload rather than the filtered rows, so the\n * chips keep showing the full picture while one of them is applied — a filter\n * whose own label changed when you used it would make it impossible to see how\n * much you had excluded.\n * @param reports - every report in the payload.\n * @returns the counts.\n */\nexport function statusCounts(reports: readonly ReportFrontMatter[]): StatusCounts {\n const counts = { all: reports.length, open: 0, acked: 0, closed: 0 }\n for (const front of reports) {\n if (front.status === 'open') counts.open += 1\n else if (front.status === 'acked') counts.acked += 1\n else if (front.status === 'closed') counts.closed += 1\n }\n return counts\n}\n\n/**\n * Whether a filter would hide anything.\n * @param filter - the active filter.\n * @returns true when a status or text constraint is in force.\n */\nexport function isFiltering(filter: RowFilter): boolean {\n return filter.status !== 'all' || filter.text.trim() !== ''\n}\n\n/**\n * Index the payload's working-tree lanes by report id.\n *\n * The lanes carry the lease state, which the digests do not: liveness is derived\n * from the hop stream, and the host derives it once rather than each half of the\n * plugin deriving it separately.\n * @param payload - the endpoint payload.\n * @returns report id to lane.\n */\nexport function laneIndex(payload: TimelinePayload): Map<string, WorktreeLane> {\n return new Map(payload.worktrees.map((lane) => [lane.report, lane]))\n}\n\n/**\n * How a lease state should read at a glance.\n *\n * A live claim is the only state that means someone is working *now*, so it is\n * the only one that earns a positive tone. A cleared checkout is deliberately\n * plain rather than alarming: the ledger tidying up after itself is not a\n * problem to be surfaced, and giving it a warning tone would teach a reader to\n * ignore warnings.\n */\nexport const LEASE_TONE: Record<LeaseState, 'success' | 'info' | 'neutral'> = {\n held: 'success',\n released: 'info',\n reclaimed: 'neutral',\n none: 'neutral',\n}\n\n/** The last path segment, for naming a checkout that has no branch. */\nfunction lastSegment(path: string): string {\n const parts = path.split(/[\\\\/]/)\n return parts[parts.length - 1] || path\n}\n\n/**\n * The short name a checkout is shown under.\n *\n * Taken from the binding rather than from the lane, because a report opened by\n * id can be rendered before it is in the payload's lane list — the binding is\n * always on the row, the lane is not.\n * @param binding - the row's binding, when it has one.\n * @returns the label, or `undefined` when the report is not bound.\n */\nexport function worktreeLabel(binding: ReportWorktree | undefined): string | undefined {\n if (binding === undefined) return undefined\n return binding.branch ?? lastSegment(binding.path)\n}\n\n/**\n * Count how many report rows are visible under a filter.\n * @param rows - every assembled row.\n * @param filter - the active filter.\n * @returns the number of visible report rows.\n */\nexport function visibleReports(rows: readonly Row[], filter: RowFilter): number {\n let count = 0\n for (const row of filterRows(rows, filter)) if (row.kind === 'report') count += 1\n return count\n}\n\n/** Which \"there is nothing here to show\" message a payload and filter call for. */\nexport type EmptyState = 'none' | 'no-reports' | 'filtered-out'\n\n/**\n * Decide which empty-state message applies.\n *\n * An empty ledger and a filter that matched nothing look identical on screen but\n * mean opposite things: one says \"nothing has happened in this tree yet\", the\n * other says \"you are looking at a subset of what happened\". Saying the wrong\n * one makes a working ledger look broken, so the choice is made here — where it\n * is pinned by tests — rather than inline in the view.\n *\n * Counted on REPORT rows only. Session rows always survive a status chip, so\n * counting them would let a tree full of sessions report itself as \"you have\n * filtered everything out\" while the actual reason is that no report exists.\n *\n * The `no-reports` case deliberately wins over the filter: when the ledger holds\n * nothing, a filter cannot be the reason nothing is on screen, and blaming it\n * would send the reader looking for a filter to clear that they never set.\n * @param payload - the endpoint payload.\n * @param rows - every assembled row.\n * @param filter - the active filter.\n * @returns which message the view should show, if any.\n */\nexport function emptyState(payload: TimelinePayload, rows: readonly Row[], filter: RowFilter): EmptyState {\n if (payload.reports.length === 0) return 'no-reports'\n if (!isFiltering(filter)) return 'none'\n return visibleReports(rows, filter) === 0 ? 'filtered-out' : 'none'\n}\n\n/**\n * The report ids present in one payload.\n *\n * A thread link can point outside the subtree — a report answering one opened by\n * an unrelated session, for instance — and the detail endpoint is ledger-scoped,\n * so such a report still opens. Knowing which ids are in the list is what lets\n * the view say so instead of silently showing a card that is not in the timeline.\n * @param payload - the endpoint payload.\n * @returns the ids of every report in the payload.\n */\nexport function reportIds(payload: TimelinePayload): ReadonlySet<string> {\n const ids = new Set<string>()\n for (const front of payload.reports) ids.add(front.report)\n return ids\n}\n\n/** Body length above which the detail panel shows only a head. */\nexport const BODY_DISPLAY_LIMIT = 4000\n\n/** How much of an over-long body is shown. */\nexport const BODY_HEAD_LIMIT = 1200\n\n/**\n * Trim a body for the detail panel.\n *\n * Mirrors what `report_read` already does for the model: the full text stays in\n * the ledger file, and both readers get a bounded view plus a pointer rather than\n * an unbounded paste. Keeping the human view and the model view bounded the same\n * way means neither can be surprised by what the other sees.\n * @param body - the raw body.\n * @param limit - length above which truncation applies.\n * @param head - how many characters to keep when truncating.\n * @returns the display text and whether it was cut.\n */\nexport function displayBody(\n body: string,\n limit: number = BODY_DISPLAY_LIMIT,\n head: number = BODY_HEAD_LIMIT,\n): { readonly text: string; readonly truncated: boolean } {\n if (body.length <= limit) return { text: body, truncated: false }\n return { text: body.slice(0, head), truncated: true }\n}\n\n/**\n * Close a code fence left hanging by truncation.\n *\n * Cutting Markdown at a character count is not the same operation as cutting\n * plain text: the result can be a document that does not parse as written. The\n * one construct that changes the meaning of everything after it is a code fence,\n * so a body cut inside a fenced block hands the renderer an unterminated one and\n * leaves it to decide where the block ends.\n *\n * The renderer here does cope (measured: a body cut mid-fence renders as one\n * closed code block with no trailing artifact), which is exactly why this is\n * cheap insurance rather than a fix for something visibly broken: the renderer is\n * a shell platform module this plugin does not own, and its leniency is not a\n * contract. Balancing the document costs one line and makes the plugin's output\n * valid Markdown no matter who renders it.\n *\n * Balanced text is returned untouched, so this only ever adds the fence the cut\n * removed. Only backtick fences are counted; `~~~` fences are rare enough in\n * report bodies that guessing which convention the author used would be worse\n * than leaving them alone.\n * @param text - the already-truncated head.\n * @returns the head, with an unclosed fence closed.\n */\nexport function closeOpenFence(text: string): string {\n let open = false\n for (const line of text.split('\\n')) {\n if (line.trimStart().startsWith('```')) open = !open\n }\n return open ? `${text}\\n\\`\\`\\`` : text\n}\n","/**\n * The card canvas's layout: sessions as **frames**, reports as **cards**, and the\n * transfer path as **wires** between them.\n *\n * This is the successor to `topology-model.ts`. The swimlane proved that the\n * shape of a collaboration can be drawn, but its nodes are chips: a report is an\n * `R-0001` and nothing more. Here a report is a rectangle with room for its\n * subject, so the drawing carries content instead of only structure — and a\n * session becomes a **frame that contains** its reports, so belonging is drawn by\n * containment rather than inferred from a column.\n *\n * Three rules keep this module the same kind of thing as the swimlane's — pure,\n * DOM-free, byte-deterministic for one input, and pinned by assertions:\n *\n * 1. **Column = depth, vertical order = the ledger's own order.** The payload's\n * session list is already a DFS pre-order, so a frame's column is its depth\n * and its y comes from that order. No graph algorithm is involved, for the\n * same reason as before: there are no ranks to compute.\n * 2. **Card size is a level-of-detail constant, never a text measurement.** Text\n * is wrapped and elided by the painter, against whatever room the card has.\n * Measuring here would make the layout depend on a canvas context, and the\n * only claim this design has to defend — \"how many objects are on screen at\n * this zoom\" — would stop being testable. The caller picks the tier from the\n * zoom (`lodFor`) and passes it in; geometry therefore changes per tier and\n * not per zoom pixel, which is also what keeps it from jittering mid-gesture.\n * 3. **Every endpoint is known before anything is drawn.** Ends are either a\n * card edge or a **port** on a frame edge, and ports are spread by an\n * order-preserving rule (below) so two wires never land on the same pixel.\n *\n * @module dsh-report-ledger/client/cardgraph-model\n */\n\nimport type { ReportFrontMatter, TimelinePayload } from '../shared/wire.ts'\nimport { shortId } from './timeline-model.ts'\n\n/** How much of a card there is room to draw, as a function of zoom. */\nexport type CardgraphLod = 'card' | 'compact' | 'chip'\n\n/** A card's rectangle at one level of detail. */\nexport interface CardSize {\n readonly width: number\n readonly height: number\n}\n\n/** An axis-aligned rectangle, the one shape culling and hit-testing work on. */\nexport interface Rect {\n readonly x: number\n readonly y: number\n readonly width: number\n readonly height: number\n}\n\n/** Which way an end faces. */\nexport type CardgraphSide = 'left' | 'right' | 'top' | 'bottom'\n\n/** Geometry knobs. Numbers, not styles: the painter decides how to paint them. */\nexport interface CardgraphOptions {\n /** Level of detail to lay out at. Pick it with {@link lodFor}. */\n readonly lod: CardgraphLod\n /** Zoom at or above which reports are drawn as full cards. */\n readonly cardMinZoom: number\n /** Zoom at or above which reports are drawn as compact cards. */\n readonly compactMinZoom: number\n /**\n * Card size per tier.\n *\n * A whole record rather than a partial one, so \"which sizes are in play\" is one\n * value that can be replaced wholesale by a test.\n */\n readonly sizes: Readonly<Record<CardgraphLod, CardSize>>\n /** Height of a frame's title bar. */\n readonly frameHeaderHeight: number\n /** Inset from a frame's side to its cards. */\n readonly framePaddingX: number\n /** Inset from the title bar down to the first card. */\n readonly framePaddingTop: number\n /** Inset below the last card. */\n readonly framePaddingBottom: number\n /** A frame is never narrower than this, even with no cards. */\n readonly minFrameWidth: number\n /** A frame is never shorter than this, so an empty session still reads as a box. */\n readonly minFrameHeight: number\n /** Vertical gap between two stacked cards. */\n readonly cardGap: number\n /** Horizontal gap between two columns. */\n readonly columnGap: number\n /** Vertical gap between two frames in one column. */\n readonly frameGap: number\n /** Margin around the whole drawing. */\n readonly margin: number\n}\n\n/**\n * Defaults.\n *\n * A full card is 240x110 — enough for a title, a status line and two lines of\n * subject at a readable size. The chip tier is deliberately tiny: below 0.4 zoom\n * a card is a colour, and drawing readable text there would be a lie about how\n * much of the drawing the reader can actually take in.\n */\nexport const DEFAULT_CARDGRAPH_OPTIONS: CardgraphOptions = {\n lod: 'card',\n cardMinZoom: 0.8,\n compactMinZoom: 0.4,\n sizes: {\n card: { width: 240, height: 110 },\n compact: { width: 160, height: 36 },\n chip: { width: 60, height: 14 },\n },\n frameHeaderHeight: 26,\n framePaddingX: 12,\n framePaddingTop: 10,\n framePaddingBottom: 12,\n minFrameWidth: 180,\n minFrameHeight: 96,\n cardGap: 8,\n columnGap: 48,\n frameGap: 24,\n margin: 24,\n}\n\n/** One session's frame. */\nexport interface CardgraphFrame {\n /** Session id, absent for the shared frame of out-of-tree sessions. */\n readonly id?: string\n /** Position in {@link CardgraphLayout.frames}; also how wires name a frame. */\n readonly index: number\n /** Shortened session id for the title bar. */\n readonly shortId: string\n /** Resolved title, when the payload carried one. */\n readonly title?: string\n /** Depth in the subtree, which is also the column. */\n readonly depth: number\n /** Column this frame sits in. */\n readonly column: number\n /** Whether the session is currently resident. */\n readonly live: boolean\n /** Whether this is the shared frame for sessions outside the subtree. */\n readonly external: boolean\n /** How many cards it holds. */\n readonly cards: number\n readonly bounds: Rect\n}\n\n/** One column of the drawing: all frames of one subtree depth. */\nexport interface CardgraphColumn {\n /** The depth this column holds. */\n readonly depth: number\n readonly index: number\n readonly bounds: Rect\n}\n\n/** One report, as a rectangle. */\nexport interface CardgraphCard {\n readonly report: string\n readonly subject: string\n readonly status: ReportFrontMatter['status']\n readonly task?: string\n /** Index of the frame that contains it. */\n readonly frame: number\n /** Column it sits in, i.e. the depth of its author's session. */\n readonly column: number\n /** Stacking index inside its frame, counting from the top. */\n readonly slot: number\n /** The tier this card was laid out at, so the painter knows what fits. */\n readonly lod: CardgraphLod\n readonly bounds: Rect\n}\n\n/** What a wire means. */\nexport type CardgraphEdgeKind = 'to' | 'cc' | 'author' | 'thread'\n\n/**\n * One end of a wire.\n *\n * `card` ends are anchored to a card's edge, at the middle of that edge. `frame`\n * ends are **ports**: a frame edge is shared by every wire that touches it, so\n * the y is assigned by the spreading pass rather than by any single wire.\n */\nexport interface CardgraphEnd {\n readonly kind: 'card' | 'frame'\n /** Report id for a card end, session id for a frame end (absent = out of tree). */\n readonly id?: string\n /** Index into `cards` for a card end, into `frames` for a frame end. */\n readonly index: number\n readonly side: CardgraphSide\n readonly x: number\n readonly y: number\n}\n\n/** One drawn relation. */\nexport interface CardgraphEdge {\n /** The report the wire belongs to. */\n readonly report: string\n readonly kind: CardgraphEdgeKind\n /** Where the wire leaves; carries the arrowhead's opposite end. */\n readonly from: CardgraphEnd\n /** Where the wire lands; the arrowhead goes here. */\n readonly to: CardgraphEnd\n /** Whether either end is the out-of-tree frame. */\n readonly external: boolean\n /**\n * The x a thread runs down between columns.\n *\n * Only thread wires have one. A thread spans frames, and drawing it straight\n * would cross whatever sits in between; routing it down a **column gutter**\n * keeps it in the margin band, where only other wires are.\n */\n readonly viaX?: number\n /**\n * The wire's bounding box: both ends, plus the gutter a thread bends through.\n *\n * Culling works on this, which is why a wire whose ends are both off-screen but\n * whose middle crosses the viewport is still drawn.\n */\n readonly bounds: Rect\n}\n\n/** The whole drawing, as numbers. */\nexport interface CardgraphLayout {\n /** The tier this drawing was laid out at. */\n readonly lod: CardgraphLod\n readonly columns: readonly CardgraphColumn[]\n readonly frames: readonly CardgraphFrame[]\n /** In frame order, then top to bottom, so the order is fully determined. */\n readonly cards: readonly CardgraphCard[]\n readonly edges: readonly CardgraphEdge[]\n /** Every frame is this wide, so the columns line up. */\n readonly frameWidth: number\n readonly cardWidth: number\n readonly cardHeight: number\n readonly width: number\n readonly height: number\n}\n\n/**\n * Which tier a zoom level calls for.\n *\n * Thresholds are inclusive on the low end, so 0.8 is a full card and 0.4 is a\n * compact one: a zoom that lands exactly on a step reads as the richer of the\n * two. A zoom that is not a usable number degrades to the cheapest tier instead\n * of producing an infinite or negative box.\n * @param zoom - the current scale.\n * @param options - threshold overrides.\n * @returns the tier to lay out at.\n */\nexport function lodFor(zoom: number, options: Partial<CardgraphOptions> = {}): CardgraphLod {\n const opts: CardgraphOptions = { ...DEFAULT_CARDGRAPH_OPTIONS, ...options }\n if (!Number.isFinite(zoom)) return 'chip'\n if (zoom >= opts.cardMinZoom) return 'card'\n if (zoom >= opts.compactMinZoom) return 'compact'\n return 'chip'\n}\n\n/**\n * The three tiers in the order a zoom sweep visits them, richest first.\n *\n * Exported so a painter can pre-build its text caches per tier without hardcoding\n * the list in a second place.\n */\nexport const CARDGRAPH_LODS: readonly CardgraphLod[] = ['card', 'compact', 'chip']\n\n/** @returns the card size of one tier. */\nexport function cardSize(lod: CardgraphLod, options: Partial<CardgraphOptions> = {}): CardSize {\n const opts: CardgraphOptions = { ...DEFAULT_CARDGRAPH_OPTIONS, ...options }\n return opts.sizes[lod]\n}\n\n/** Whether two rectangles overlap. Touching edges do not count as overlapping. */\nexport function intersects(a: Rect, b: Rect): boolean {\n return a.x < b.x + b.width && b.x < a.x + a.width && a.y < b.y + b.height && b.y < a.y + a.height\n}\n\n/** Whether a point is inside a rectangle. The top-left edges are inside, the bottom-right ones are not. */\nexport function contains(rect: Rect, x: number, y: number): boolean {\n return x >= rect.x && x < rect.x + rect.width && y >= rect.y && y < rect.y + rect.height\n}\n\n/** Grow a rectangle by `by` on every side. */\nexport function grow(rect: Rect, by: number): Rect {\n return { x: rect.x - by, y: rect.y - by, width: rect.width + by * 2, height: rect.height + by * 2 }\n}\n\n/**\n * Keep the items whose box meets the viewport.\n *\n * This is the card canvas's replacement for the swimlane's row window: with a\n * free 2D canvas there are no rows to count, so \"what is on screen\" is decided by\n * rectangle overlap. `bounds` is the only field it looks at, which is why cards,\n * frames and wires all expose one.\n * @param items - anything with a bounding box.\n * @param viewport - the visible rect, **in drawing coordinates**.\n * @returns the items to draw, in the order they were given.\n */\nexport function cullByBounds<T extends { readonly bounds: Rect }>(\n items: readonly T[],\n viewport: Rect,\n): readonly T[] {\n return items.filter((item) => intersects(item.bounds, viewport))\n}\n\n/**\n * The topmost item under a point.\n *\n * Free of the DOM on purpose: the canvas has no elements to hit, so picking is a\n * function from a point and an ordered list to an item — and that function is\n * testable here rather than only by clicking in a browser. The caller passes the\n * list **topmost first**, which is what makes \"a card wins over the frame holding\n * it\" a property of the order rather than of the arithmetic.\n * @param items - candidates, topmost first.\n * @param x - point, in drawing coordinates.\n * @param y - point, in drawing coordinates.\n * @returns the first hit, or `undefined`.\n */\nexport function hitTest<T extends { readonly bounds: Rect }>(\n items: readonly T[],\n x: number,\n y: number,\n): T | undefined {\n return items.find((item) => contains(item.bounds, x, y))\n}\n\n/** A pan/zoom transform plus the size of the box it paints into. */\nexport interface CardgraphViewport {\n /** Pan, in screen pixels. */\n readonly offsetX: number\n readonly offsetY: number\n /** Scale, screen pixels per drawing unit. */\n readonly scale: number\n /** Width of the paint box, in screen pixels. */\n readonly width: number\n /** Height of the paint box, in screen pixels. */\n readonly height: number\n}\n\n/**\n * The part of the drawing a viewport can see, in drawing coordinates.\n *\n * The inverse of the transform the painter applies, and the input to culling. A\n * broken transform is treated as identity rather than propagated: a scale of zero\n * or a NaN would otherwise turn the visible rect into an infinite or reversed box\n * and either cull everything or draw everything.\n * @param view - the transform and the size of the paint box.\n * @param overscan - drawing units to keep on each side, so a pan does not reveal a\n * blank band before the next frame is painted.\n * @returns the visible rect.\n */\nexport function worldViewport(view: CardgraphViewport, overscan = 0): Rect {\n const scale = Number.isFinite(view.scale) && view.scale > 0 ? view.scale : 1\n const width = Number.isFinite(view.width) ? Math.max(0, view.width) : 0\n const height = Number.isFinite(view.height) ? Math.max(0, view.height) : 0\n const offsetX = Number.isFinite(view.offsetX) ? view.offsetX : 0\n const offsetY = Number.isFinite(view.offsetY) ? view.offsetY : 0\n const rect: Rect = {\n x: -offsetX / scale,\n y: -offsetY / scale,\n width: width / scale,\n height: height / scale,\n }\n return overscan === 0 ? rect : grow(rect, overscan)\n}\n\n/** A mutable end, while ports are still being spread. */\ninterface PendingEnd {\n kind: 'card' | 'frame'\n id?: string\n index: number\n side: CardgraphSide\n x: number\n y: number\n}\n\n/** A wire before its ports have y coordinates. */\ninterface PendingEdge {\n report: string\n kind: CardgraphEdgeKind\n from: PendingEnd\n to: PendingEnd\n external: boolean\n viaX?: number\n}\n\n/**\n * Build the card graph.\n *\n * Frames come from the payload's sessions: column by subtree depth, vertical\n * order by the payload's own order (the DFS pre-order the list indents by), so a\n * session keeps its relative position across the two views. Cards are stacked\n * inside their author's frame **by time**, because a frame is one session's own\n * timeline; the reader who wants one timeline across all sessions has the list\n * below. Only the reports handed in are drawn, so this is a filtered view rather\n * than a second source of truth.\n * @param payload - the endpoint payload, for the session frames.\n * @param reports - the reports to plot; any order, since each frame sorts its own.\n * @param options - geometry and tier overrides.\n * @returns the layout; empty (and zero-sized) when there is nothing to draw.\n */\nexport function buildCardgraph(\n payload: TimelinePayload,\n reports: readonly ReportFrontMatter[],\n options: Partial<CardgraphOptions> = {},\n): CardgraphLayout {\n const opts: CardgraphOptions = { ...DEFAULT_CARDGRAPH_OPTIONS, ...options }\n const size = opts.sizes[opts.lod]\n const frameWidth = Math.max(opts.minFrameWidth, size.width + opts.framePaddingX * 2)\n\n // -------------------------------------------------------------------------\n // Frames\n // -------------------------------------------------------------------------\n const sessions = payload.sessions\n const depths = [...new Set(sessions.map((node) => node.depth))].sort((a, b) => a - b)\n const columnOfDepth = new Map<number, number>()\n for (const [index, depth] of depths.entries()) columnOfDepth.set(depth, index)\n\n const frameOf = new Map<string, number>()\n const frames: {\n id?: string\n shortId: string\n title?: string\n depth: number\n column: number\n live: boolean\n external: boolean\n cards: number\n }[] = sessions.map((node, index) => {\n frameOf.set(node.id, index)\n return {\n ...(node.title === undefined ? {} : { title: node.title }),\n id: node.id,\n shortId: shortId(node.id),\n depth: node.depth,\n column: columnOfDepth.get(node.depth) ?? 0,\n live: node.live,\n external: false,\n cards: 0,\n }\n })\n\n // One shared frame for every session this subtree does not contain, so a report\n // copied in from another line still has somewhere to land. Created on demand:\n // a payload whose reports never leave the tree gets no such frame.\n const mentions = (front: ReportFrontMatter): readonly (string | undefined)[] =>\n [front.from, ...front.to, ...front.cc, ...front.authors]\n const needsExternal = reports.some((front) => mentions(front).some((id) => id !== undefined && !frameOf.has(id)))\n const externalDepth = sessions.reduce((max, node) => Math.max(max, node.depth), -1) + 1\n if (needsExternal) {\n frames.push({\n shortId: '',\n depth: externalDepth,\n column: depths.length,\n live: false,\n external: true,\n cards: 0,\n })\n }\n const externalIndex = needsExternal ? frames.length - 1 : -1\n const frameIndexById = (id: string | undefined): number =>\n id !== undefined && frameOf.has(id) ? (frameOf.get(id) as number) : externalIndex\n\n // -------------------------------------------------------------------------\n // Cards, stacked by time inside their frame\n // -------------------------------------------------------------------------\n const buckets: ReportFrontMatter[][] = frames.map(() => [])\n for (const front of reports) {\n const target = frameIndexById(front.from)\n if (target >= 0) buckets[target]?.push(front)\n }\n // Time order, with the report id as the tie-break so two reports created in the\n // same millisecond do not swap places between builds.\n for (const bucket of buckets) {\n bucket.sort((a, b) => a.created - b.created || (a.report < b.report ? -1 : a.report > b.report ? 1 : 0))\n }\n\n // -------------------------------------------------------------------------\n // Frame geometry\n // -------------------------------------------------------------------------\n const columnX = (column: number): number => opts.margin + column * (frameWidth + opts.columnGap)\n const frameHeight = (count: number): number => Math.max(\n opts.minFrameHeight,\n opts.frameHeaderHeight + opts.framePaddingTop + opts.framePaddingBottom\n + count * size.height + Math.max(0, count - 1) * opts.cardGap,\n )\n\n // The counts have to land before the boxes do: a frame's height is a function of\n // how many cards it holds, and a height that disagreed with the stacking would\n // put a card outside its own frame.\n frames.forEach((frame, index) => { frame.cards = buckets[index]?.length ?? 0 })\n\n const cursor = new Map<number, number>()\n const fixed: Rect[] = frames.map((frame) => {\n const height = frameHeight(frame.cards)\n const y = cursor.get(frame.column) ?? opts.margin\n cursor.set(frame.column, y + height + opts.frameGap)\n return { x: columnX(frame.column), y, width: frameWidth, height }\n })\n\n const columnCount = depths.length + (needsExternal ? 1 : 0)\n const heights = Array.from({ length: columnCount }, (_unused, column) => {\n const stacked = fixed.filter((_box, index) => frames[index]?.column === column)\n return stacked.length === 0\n ? 0\n : stacked.reduce((sum, box) => sum + box.height, 0) + (stacked.length - 1) * opts.frameGap\n })\n\n const layoutFrames: CardgraphFrame[] = frames.map((frame, index) => ({\n ...(frame.id === undefined ? {} : { id: frame.id }),\n ...(frame.title === undefined ? {} : { title: frame.title }),\n index,\n shortId: frame.shortId,\n depth: frame.depth,\n column: frame.column,\n live: frame.live,\n external: frame.external,\n cards: frame.cards,\n bounds: fixed[index] as Rect,\n }))\n\n const columns: CardgraphColumn[] = Array.from({ length: columnCount }, (_unused, column) => ({\n depth: depths[column] ?? externalDepth,\n index: column,\n bounds: {\n x: columnX(column),\n y: opts.margin,\n width: frameWidth,\n height: Math.max(0, heights[column] ?? 0),\n },\n }))\n\n // -------------------------------------------------------------------------\n // Card geometry\n // -------------------------------------------------------------------------\n const cards: CardgraphCard[] = []\n const cardIndexOf = new Map<string, number>()\n layoutFrames.forEach((frame) => {\n const bucket = buckets[frame.index] ?? []\n bucket.forEach((front, slot) => {\n cardIndexOf.set(front.report, cards.length)\n cards.push({\n report: front.report,\n subject: front.subject,\n status: front.status,\n ...(front.task === undefined ? {} : { task: front.task }),\n frame: frame.index,\n column: frame.column,\n slot,\n lod: opts.lod,\n bounds: {\n x: frame.bounds.x + opts.framePaddingX,\n y: frame.bounds.y + opts.frameHeaderHeight + opts.framePaddingTop + slot * (size.height + opts.cardGap),\n width: size.width,\n height: size.height,\n },\n })\n })\n })\n\n // -------------------------------------------------------------------------\n // Wires\n // -------------------------------------------------------------------------\n const cardEnd = (index: number, side: CardgraphSide): PendingEnd => {\n const box = cards[index]?.bounds ?? { x: 0, y: 0, width: 0, height: 0 }\n return {\n kind: 'card',\n id: cards[index]?.report,\n index,\n side,\n x: side === 'right' ? box.x + box.width : side === 'left' ? box.x : box.x + box.width / 2,\n y: side === 'bottom' ? box.y + box.height : side === 'top' ? box.y : box.y + box.height / 2,\n }\n }\n const frameEnd = (index: number, side: CardgraphSide): PendingEnd => {\n const box = fixed[index] ?? { x: 0, y: 0, width: 0, height: 0 }\n return {\n kind: 'frame',\n ...(frames[index]?.id === undefined ? {} : { id: frames[index]?.id }),\n index,\n side,\n x: side === 'right' ? box.x + box.width : box.x,\n y: box.y + box.height / 2,\n }\n }\n const externalBetween = (a: number, b: number): boolean =>\n (externalIndex >= 0 && (a === externalIndex || b === externalIndex)) || false\n\n const pending: PendingEdge[] = []\n\n /**\n * The side a wire should bend through to reach another frame without crossing\n * one.\n *\n * Two sessions in the **same column** — siblings, or any two frames at one\n * depth — cannot be reached left-to-right: a straight line from the sender's\n * card to the recipient's facing edge runs back across the recipient's own box,\n * which reads as \"this wire belongs to that frame\". Such a wire bends down the\n * gutter beside the column instead: out of the card, along the gutter, into the\n * frame's edge on that same side. The gutter is on the right except in the last\n * column, where that would run along the outside of the drawing.\n */\n const gutterSide = (column: number): CardgraphSide => (column < columns.length - 1 ? 'right' : 'left')\n const gutterX = (column: number, side: CardgraphSide): number => {\n const box = columns[column]?.bounds ?? { x: 0, y: 0, width: 0, height: 0 }\n return side === 'right' ? box.x + box.width + opts.columnGap / 2 : box.x - opts.columnGap / 2\n }\n\n // Wires are walked in **card order**, not in the order the caller passed the\n // reports: the drawing is then a function of the set of reports rather than of\n // the order they arrived in, which is what makes \"same ledger, same picture\"\n // testable with a shuffled input.\n const frontByReport = new Map(reports.map((front) => [front.report, front]))\n for (const [cardIndex, card] of cards.entries()) {\n const front = frontByReport.get(card.report)\n if (front === undefined) continue\n const ownFrame = card.frame\n const ownColumn = card.column\n\n // Deliveries leave the card and land on the recipient's frame, on the side\n // facing the sender. The digest carries no per-hop times (those live in the\n // route sidecar), so the landing y is the port's business, not the wire's.\n for (const [kind, ids] of [['to', front.to], ['cc', front.cc]] as const) {\n for (const id of new Set(ids)) {\n const target = frameIndexById(id)\n // Nothing to draw for a report delivered to its own session, and nothing\n // to draw to a session this drawing does not have a frame for.\n if (target < 0 || target === ownFrame) continue\n const targetColumn = frames[target]?.column ?? 0\n const sameColumn = targetColumn === ownColumn\n const side = sameColumn\n ? gutterSide(ownColumn)\n : targetColumn > ownColumn ? 'right' : 'left'\n pending.push({\n report: front.report,\n kind,\n from: cardEnd(cardIndex, side),\n to: frameEnd(target, sameColumn ? side : side === 'right' ? 'left' : 'right'),\n external: externalBetween(ownFrame, target),\n ...(sameColumn ? { viaX: gutterX(ownColumn, side) } : {}),\n })\n }\n }\n\n // Co-authorship reads the other way round: the co-author's frame reaches the\n // card, and the card is the end with the arrowhead.\n for (const id of new Set(front.authors)) {\n if (id === front.from) continue\n const source = frameIndexById(id)\n if (source < 0 || source === ownFrame) continue\n const sourceColumn = frames[source]?.column ?? 0\n const sameColumn = sourceColumn === ownColumn\n const side = sameColumn\n ? gutterSide(ownColumn)\n : sourceColumn < ownColumn ? 'right' : 'left'\n pending.push({\n report: front.report,\n kind: 'author',\n from: frameEnd(source, side),\n to: cardEnd(cardIndex, sameColumn ? side : side === 'right' ? 'left' : 'right'),\n external: externalBetween(source, ownFrame),\n ...(sameColumn ? { viaX: gutterX(ownColumn, side) } : {}),\n })\n }\n\n // Only when both ends are plotted: a parent that was filtered out or lives in\n // another tree is the detail panel's business, not the drawing's.\n const parentIndex = front.parent === undefined || front.parent === front.report\n ? undefined\n : cardIndexOf.get(front.parent)\n const parentCard = parentIndex === undefined ? undefined : cards[parentIndex]\n if (parentIndex !== undefined && parentCard !== undefined) {\n const from = cardEnd(parentIndex, 'bottom')\n const to = cardEnd(cardIndex, 'top')\n const column = parentCard.column\n const target = card.column\n // Route down the gutter on the side the child is on; a thread inside one\n // column takes the same detour as a same-column delivery, for the same\n // reason.\n const side = target > column ? 'right' : target < column ? 'left' : gutterSide(column)\n pending.push({\n report: front.report,\n kind: 'thread',\n from,\n to,\n external: externalBetween(parentCard.frame, ownFrame),\n viaX: gutterX(column, side),\n })\n }\n }\n\n // -------------------------------------------------------------------------\n // Ports: spread every wire that shares a frame edge, in the order of the far end\n // -------------------------------------------------------------------------\n const groups = new Map<string, { end: PendingEnd; order: number; tie: string }[]>()\n for (const edge of pending) {\n for (const end of [edge.from, edge.to]) {\n if (end.kind !== 'frame') continue\n const other = end === edge.from ? edge.to : edge.from\n const key = `${end.index}|${end.side}`\n const list = groups.get(key) ?? []\n list.push({ end, order: other.y, tie: `${edge.report}|${edge.kind}` })\n groups.set(key, list)\n }\n }\n for (const list of groups.values()) {\n list.sort((a, b) => a.order - b.order || (a.tie < b.tie ? -1 : a.tie > b.tie ? 1 : 0))\n const box = fixed[list[0]?.end.index ?? 0] ?? { x: 0, y: 0, width: 0, height: 0 }\n const top = box.y + opts.frameHeaderHeight + opts.framePaddingTop\n const bottom = box.y + box.height - opts.framePaddingBottom\n const span = bottom - top\n list.forEach((entry, index) => {\n // Evenly inside the card band, which keeps every port off both the title bar\n // and the frame's own edge. A frame too short for a band puts them on its\n // middle line rather than stacking them on one another.\n entry.end.y = span > 0 ? top + ((index + 1) * span) / (list.length + 1) : box.y + box.height / 2\n })\n }\n\n const edges: CardgraphEdge[] = pending.map((edge) => {\n const xs = [edge.from.x, edge.to.x, ...(edge.viaX === undefined ? [] : [edge.viaX])]\n const ys = [edge.from.y, edge.to.y]\n const minX = Math.min(...xs)\n const minY = Math.min(...ys)\n return {\n report: edge.report,\n kind: edge.kind,\n from: { ...edge.from },\n to: { ...edge.to },\n external: edge.external,\n ...(edge.viaX === undefined ? {} : { viaX: edge.viaX }),\n bounds: {\n x: minX,\n y: minY,\n width: Math.max(...xs) - minX,\n height: Math.max(...ys) - minY,\n },\n }\n })\n\n const contentWidth = columnCount === 0\n ? 0\n : opts.margin * 2 + columnCount * frameWidth + (columnCount - 1) * opts.columnGap\n const widest = heights.reduce((max, height) => Math.max(max, height), 0)\n\n return {\n lod: opts.lod,\n columns,\n frames: layoutFrames,\n cards,\n edges,\n frameWidth,\n cardWidth: size.width,\n cardHeight: size.height,\n width: columnCount === 0 ? 0 : contentWidth,\n height: columnCount === 0 ? 0 : opts.margin * 2 + widest,\n }\n}\n","/**\n * Painting the card canvas.\n *\n * The layout decides *what* is where; this module decides what a card, a frame\n * and a wire look like, and pushes them at a 2D context. Two choices carry the\n * design:\n *\n * 1. **The painter talks to a structural context, not to a canvas.** Everything\n * it needs is the small `PaintContext` surface below — fills, paths, text,\n * one transform — so the same painter drives a canvas, a recorder in a test,\n * or an SVG surface, and nothing here can reach for the DOM. This is the part\n * of draw.io's architecture worth copying: its shapes only ever talk to an\n * `AbstractCanvas2D`, so `SvgCanvas2D` and `XmlCanvas2D` are drop-in. Ours\n * keeps the same door open for a fraction of the code.\n *\n * 2. **Text is measured here and nowhere else.** `foldText` wraps and elides\n * against a `measure` function, whose only production implementation reads the\n * context's own metrics — cached per (font, text), because every measure on a\n * canvas context is a synchronous text-layout call. The geometry never sees\n * any of it: the layout's card sizes are constants, so however long a subject\n * is, the card it sits in does not move.\n *\n * @module dsh-report-ledger/client/cardgraph-painter\n */\n\nimport type {\n CardgraphCard, CardgraphEdge, CardgraphEdgeKind, CardgraphFrame, CardgraphLayout, CardgraphViewport, Rect,\n} from './cardgraph-model.ts'\nimport { intersects, worldViewport } from './cardgraph-model.ts'\n\n/**\n * The 2D surface the painter draws through.\n *\n * A hand-picked subset of `CanvasRenderingContext2D`, which a real canvas context\n * satisfies as-is and a test can implement in twenty lines. Keeping it this small\n * is deliberate: every method here is one a hypothetical SVG painter must also\n * provide, and anything outside it would be a canvas-only shortcut.\n */\nexport interface PaintContext {\n save(): void\n restore(): void\n setTransform(a: number, b: number, c: number, d: number, e: number, f: number): void\n clearRect(x: number, y: number, width: number, height: number): void\n fillRect(x: number, y: number, width: number, height: number): void\n beginPath(): void\n closePath(): void\n moveTo(x: number, y: number): void\n lineTo(x: number, y: number): void\n arc(x: number, y: number, radius: number, startAngle: number, endAngle: number): void\n arcTo(x1: number, y1: number, x2: number, y2: number, radius: number): void\n clip(): void\n fill(): void\n stroke(): void\n fillText(text: string, x: number, y: number): void\n measureText(text: string): { readonly width: number }\n setLineDash(segments: readonly number[]): void\n translate(x: number, y: number): void\n scale(x: number, y: number): void\n fillStyle: string\n strokeStyle: string\n lineWidth: number\n lineDashOffset: number\n globalAlpha: number\n font: string\n textAlign: CanvasTextAlign\n textBaseline: CanvasTextBaseline\n}\n\n/** One font token, resolved to what a context needs. */\nexport interface PaintFont {\n readonly size: number\n readonly weight: number\n readonly lineHeight: number\n readonly family: string\n}\n\n/**\n * The colours and fonts the painter draws with.\n *\n * Read once from the shell's `--dsw-*` tokens (`readTheme`), so the drawing\n * follows the light/dark theme without knowing it exists. Every field is a string\n * a context accepts verbatim.\n */\nexport interface PaintTheme {\n /** Page background behind the drawing. */\n readonly canvas: string\n /** Frame body. */\n readonly frame: string\n /** Frame title bar. */\n readonly frameBar: string\n /** Card body. */\n readonly surface: string\n /** Hairlines: frame borders, card borders. */\n readonly border: string\n readonly frameBorder: string\n /** Text, from loudest to quietest. */\n readonly text: string\n readonly secondary: string\n readonly caption: string\n readonly tertiary: string\n /** Accent for the focused or open card. */\n readonly accent: string\n /** Lifecycle tones, matching the tab's `Tag` tones. */\n readonly warning: string\n readonly success: string\n readonly error: string\n /** The colour threads are drawn in. */\n readonly business: string\n readonly fonts: {\n /** Frame titles. */\n readonly title: PaintFont\n /** Report ids, which want weight rather than size. */\n readonly id: PaintFont\n /** Card subjects. */\n readonly body: PaintFont\n /** Status and task, the quietest line. */\n readonly meta: PaintFont\n }\n}\n\n/** A guaranteed-visible theme, for a context with no tokens at all. */\nexport const FALLBACK_THEME: PaintTheme = {\n canvas: '#ffffff',\n frame: '#f6f7f9',\n frameBar: '#eef0f3',\n surface: '#ffffff',\n border: '#d8dbe0',\n frameBorder: '#c3c7ce',\n text: '#1a1c1f',\n secondary: '#4a4f57',\n caption: '#70757d',\n tertiary: '#9aa0a8',\n accent: '#2f6fed',\n warning: '#c77700',\n success: '#1f9254',\n error: '#d33a2c',\n business: '#2f6fed',\n fonts: {\n title: { size: 13, weight: 500, lineHeight: 18, family: 'system-ui, sans-serif' },\n id: { size: 12, weight: 600, lineHeight: 16, family: 'system-ui, sans-serif' },\n body: { size: 11, weight: 400, lineHeight: 15, family: 'system-ui, sans-serif' },\n meta: { size: 11, weight: 500, lineHeight: 15, family: 'system-ui, sans-serif' },\n },\n}\n\n/** Token name → the field it fills. Keeping it as data means one reader, not twenty. */\nconst TOKENS: readonly (readonly [keyof Omit<PaintTheme, 'fonts'>, string, string])[] = [\n ['canvas', '--dsw-alias-bg-base', FALLBACK_THEME.canvas],\n ['frame', '--dsw-alias-bg-layer-1', FALLBACK_THEME.frame],\n ['frameBar', '--dsw-alias-bg-layer-2', FALLBACK_THEME.frameBar],\n ['surface', '--dsw-alias-bg-layer-3', FALLBACK_THEME.surface],\n ['border', '--dsw-alias-border-l1', FALLBACK_THEME.border],\n ['frameBorder', '--dsw-alias-border-l2', FALLBACK_THEME.frameBorder],\n ['text', '--dsw-alias-label-primary', FALLBACK_THEME.text],\n ['secondary', '--dsw-alias-label-secondary', FALLBACK_THEME.secondary],\n ['caption', '--dsw-alias-label-caption', FALLBACK_THEME.caption],\n ['tertiary', '--dsw-alias-label-tertiary', FALLBACK_THEME.tertiary],\n ['accent', '--dsw-alias-brand-primary', FALLBACK_THEME.accent],\n ['warning', '--dsw-alias-state-warn-primary', FALLBACK_THEME.warning],\n ['success', '--dsw-alias-state-success-primary', FALLBACK_THEME.success],\n ['error', '--dsw-alias-state-error-primary', FALLBACK_THEME.error],\n ['business', '--dsw-alias-state-business-primary', FALLBACK_THEME.business],\n]\n\n/** Font token → the field it fills. Each token carries its own size, weight and family. */\nconst FONT_TOKENS: readonly (readonly [keyof PaintTheme['fonts'], string, PaintFont])[] = [\n ['title', '--dsw-font-xs-13', FALLBACK_THEME.fonts.title],\n ['id', '--dsw-font-xxs-strong-12', FALLBACK_THEME.fonts.id],\n ['body', '--dsw-font-xxxs-11', FALLBACK_THEME.fonts.body],\n ['meta', '--dsw-font-xxxs-strong-11', FALLBACK_THEME.fonts.meta],\n]\n\n/** The one token a theme change always moves, so a repaint can detect it cheaply. */\nconst CANARY = '--dsw-alias-bg-base'\n\n/** A `var()`-style read of one token, with a fallback for a shell that has none. */\ntype ReadToken = (name: string) => string\n\n/** Assemble a canvas font string. */\nexport function fontString(font: PaintFont): string {\n return `${font.weight} ${font.size}px ${font.family}`\n}\n\n/** Read one font token, taking its size/weight/line-height/family parts. */\nfunction readFont(read: ReadToken, token: string, fallback: PaintFont): PaintFont {\n const size = Number.parseFloat(read(`${token}-font-size`))\n const weight = Number.parseFloat(read(`${token}-font-weight`))\n const lineHeight = Number.parseFloat(read(`${token}-line-height`))\n const family = read(`${token}-font-family`)\n return {\n size: Number.isFinite(size) && size > 0 ? size : fallback.size,\n weight: Number.isFinite(weight) && weight > 0 ? weight : fallback.weight,\n lineHeight: Number.isFinite(lineHeight) && lineHeight > 0 ? lineHeight : fallback.lineHeight,\n family: family === '' ? fallback.family : family,\n }\n}\n\n/**\n * Read the drawing's colours and fonts out of the live theme.\n * @param read - a token reader, normally `getComputedStyle(root).getPropertyValue`.\n * @returns the theme; every field falls back so a missing token cannot blank the drawing.\n */\nexport function readTheme(read: ReadToken): PaintTheme {\n const pick = (token: string, fallback: string): string => {\n const value = read(token).trim()\n return value === '' ? fallback : value\n }\n const colours: Partial<Record<keyof Omit<PaintTheme, 'fonts'>, string>> = {}\n for (const [field, token, fallback] of TOKENS) colours[field] = pick(token, fallback)\n const fonts: Partial<Record<keyof PaintTheme['fonts'], PaintFont>> = {}\n for (const [field, token, fallback] of FONT_TOKENS) fonts[field] = readFont(read, token, fallback)\n return { ...FALLBACK_THEME, ...colours, fonts: { ...FALLBACK_THEME.fonts, ...fonts } }\n}\n\n/** A theme reader that only re-reads the tokens when the theme actually changed. */\nexport interface ThemeReader {\n /** @returns the current theme, cached until the canary token moves. */\n read(): PaintTheme\n}\n\n/**\n * Watch the theme through one token.\n *\n * `getComputedStyle(...).getPropertyValue` forces a style read, so doing twenty of\n * them per repaint would be paying for the theme on every hover. The canary is the\n * page background: if it did not move, nothing else did, and the cached theme is\n * returned.\n * @param root - the element carrying the tokens, normally `document.documentElement`.\n * @param win - the window to read styles from.\n * @returns the reader.\n */\nexport function createThemeReader(root: Element, win: Window): ThemeReader {\n const style = win.getComputedStyle(root)\n const read: ReadToken = (name) => style.getPropertyValue(name)\n let canary: string | undefined\n let cached: PaintTheme | undefined\n return {\n read(): PaintTheme {\n const current = read(CANARY)\n if (cached === undefined || current !== canary) {\n canary = current\n cached = readTheme(read)\n }\n return cached\n },\n }\n}\n\n/**\n * Shorten text until it fits, ending it with an ellipsis.\n * @param text - the text to fit.\n * @param maxWidth - the room available.\n * @param measure - width of a candidate string.\n * @returns text that fits, or `'…'` when not even one character does.\n */\nexport function elide(text: string, maxWidth: number, measure: (text: string) => number): string {\n if (measure(text) <= maxWidth) return text\n for (let take = text.length - 1; take > 0; take--) {\n const candidate = `${text.slice(0, take).trimEnd()}…`\n if (measure(candidate) <= maxWidth) return candidate\n }\n return '…'\n}\n\n/**\n * Wrap text to a width, eliding whatever does not fit in the last line.\n *\n * Character-by-character, with one concession to Latin text: when a line has to\n * break and there is a space far enough back, break at the space instead of mid\n * word. Chinese has no spaces, so a word-based rule would leave a long subject on\n * one line and elide most of it; this rule wraps it, and still avoids breaking\n * \"report-ledger\" in half when it can.\n * @param text - the text to lay out.\n * @param maxWidth - the room available.\n * @param measure - width of a candidate string.\n * @param maxLines - how many lines to produce.\n * @returns the lines, possibly empty.\n */\nexport function foldText(\n text: string,\n maxWidth: number,\n measure: (text: string) => number,\n maxLines = 1,\n): readonly string[] {\n const clean = text.replace(/\\s+/g, ' ').trim()\n if (clean === '' || maxWidth <= 0 || maxLines < 1) return []\n const lines: string[] = []\n let rest = clean\n while (rest !== '' && lines.length < maxLines) {\n if (lines.length === maxLines - 1) {\n lines.push(elide(rest, maxWidth, measure))\n break\n }\n if (measure(rest) <= maxWidth) {\n lines.push(rest)\n break\n }\n let cut = 0\n for (let take = 1; take <= rest.length; take++) {\n if (measure(rest.slice(0, take)) > maxWidth) break\n cut = take\n }\n if (cut === 0) {\n lines.push(elide(rest, maxWidth, measure))\n break\n }\n // Only back off to a space if it is at least a third of the way out: on a\n // narrow card backing off further would leave the line nearly empty.\n const space = rest.lastIndexOf(' ', cut)\n if (space > cut / 3) cut = space\n lines.push(rest.slice(0, cut).trimEnd())\n rest = rest.slice(cut).trimStart()\n }\n return lines\n}\n\n/** What the pointer is on, or what the reader opened. */\nexport interface PaintFocus {\n /** Report under the pointer. */\n readonly report?: string\n /** Frame under the pointer. */\n readonly frame?: number\n /** Report whose detail panel is open. */\n readonly selected?: string\n}\n\n/** One frame's worth of drawing instructions. */\nexport interface PaintScene {\n readonly layout: CardgraphLayout\n readonly theme: PaintTheme\n readonly viewport: CardgraphViewport\n readonly focus?: PaintFocus\n /** Localised words the drawing needs, supplied by the view. */\n readonly labels: {\n /** The shared frame's title, e.g. \"树外\". */\n readonly external: string\n /** What an empty frame says. */\n readonly empty: string\n /** Lifecycle status → its label, so a card speaks the tab's language. */\n readonly status: Readonly<Record<string, string>>\n }\n /** Extra drawing units to keep beyond the viewport, so a pan is never blank. */\n readonly overscan?: number\n}\n\n/** A surface that can draw a scene. Canvas today; an SVG implementation is a swap. */\nexport interface CardgraphPainter {\n /** Size the paint box, in CSS pixels, and the device pixel ratio to paint at. */\n resize(width: number, height: number, dpr: number): void\n /** Draw the whole drawing, at full strength and with nothing focused. */\n paint(scene: PaintScene): void\n /**\n * Draw one report's path over a scrim of everything else — the interaction\n * layer.\n *\n * This is why the drawing is split across two surfaces: a hover changes the\n * scrim and a handful of wires, never the hundreds of shapes underneath, so the\n * cost of a hover is the same on a 10-report drawing and a 3000-report one.\n * Passing no focus wipes the layer.\n * @param scene - the same scene the base layer was drawn from.\n */\n paintPath(scene: PaintScene): void\n /** Forget the text-measurement cache, e.g. when the font tokens changed. */\n reset(): void\n}\n\n/** Corner radius of a card. */\nconst CARD_RADIUS = 5\n/** Corner radius of a frame. */\nconst FRAME_RADIUS = 7\n/** Inset from a card's edge to its text. */\nconst CARD_PADDING = 9\n/** Width of the status stripe down a card's left edge. */\nconst STRIPE = 3\n/** Arrowhead length and half-width. */\nconst ARROW_LENGTH = 7\nconst ARROW_HALF = 3.5\n/**\n * How far the scrim pushes everything but the focused path back.\n *\n * **Measured, then corrected.** The first version reused the swimlane's dim value\n * (0.18 left showing) and a screenshot of the real tab showed why that was wrong\n * there and wrong here: at 0.18 the frames, their titles and every other card\n * become unreadable, so tracing one path costs the reader the context the drawing\n * exists to provide. The swimlane had already learned the same thing from the other\n * side — *the relations recede, the participants stay* — and a canvas scrim is\n * blunter than a per-shape dim, so it has to be gentler: 0.45 showing keeps every\n * frame and card legible while the focused path, drawn back on top at full\n * strength, still reads as the thing being pointed at.\n */\nconst SCRIM_ALPHA = 0.55\n/** Subject lines a full card has room for. */\nconst SUBJECT_LINES = 2\n\n/** The colour a status is drawn in, matching the tab's tones. */\nfunction statusColour(status: string, theme: PaintTheme): string {\n if (status === 'open') return theme.warning\n if (status === 'acked') return theme.success\n return theme.tertiary\n}\n\n/**\n * How each kind of wire is drawn: the token its colour comes from, its dash\n * pattern, and its weight.\n *\n * Exported because the wires are canvas and the legend is DOM — the only way the\n * two cannot drift is for the legend to read this table instead of restating it in\n * CSS. The colour is a **token name**, not a value, so it resolves through the\n * theme like everything else.\n */\nexport const WIRE_TOKENS: Readonly<Record<CardgraphEdgeKind, {\n readonly colour: string\n readonly dash: readonly number[]\n readonly width: number\n}>> = {\n to: { colour: '--dsw-alias-label-secondary', dash: [], width: 1.4 },\n cc: { colour: '--dsw-alias-label-caption', dash: [2, 3], width: 1 },\n author: { colour: '--dsw-alias-label-caption', dash: [1, 3], width: 1 },\n thread: { colour: '--dsw-alias-state-business-primary', dash: [], width: 1.4 },\n}\n\n/** Reverse lookup: which theme field a token fills. */\nconst FIELD_OF_TOKEN = new Map<string, keyof Omit<PaintTheme, 'fonts'>>(\n TOKENS.map(([field, token]) => [token, field]),\n)\n\n/** How one kind of wire is drawn, resolved against a theme. */\nfunction wireStyle(kind: CardgraphEdgeKind, theme: PaintTheme): { stroke: string; dash: readonly number[]; width: number } {\n const spec = WIRE_TOKENS[kind]\n const field = FIELD_OF_TOKEN.get(spec.colour)\n return { stroke: field === undefined ? theme.secondary : theme[field], dash: spec.dash, width: spec.width }\n}\n\n/**\n * The polyline a wire is drawn as.\n *\n * A wire with a gutter bends through it in three segments — out, along, in — which\n * is what keeps a same-column wire from crossing the frame it lands on and a\n * thread from crossing the cards between its ends.\n * @param edge - the wire.\n * @returns between two and four points, first to last.\n */\nexport function wirePoints(edge: CardgraphEdge): readonly { readonly x: number; readonly y: number }[] {\n const from = { x: edge.from.x, y: edge.from.y }\n const to = { x: edge.to.x, y: edge.to.y }\n if (edge.viaX === undefined) return [from, to]\n return [from, { x: edge.viaX, y: from.y }, { x: edge.viaX, y: to.y }, to]\n}\n\n/** Trace a rounded rectangle into the current path. */\nfunction pathRoundRect(ctx: PaintContext, rect: Rect, radius: number): void {\n const r = Math.max(0, Math.min(radius, rect.width / 2, rect.height / 2))\n ctx.beginPath()\n ctx.moveTo(rect.x + r, rect.y)\n ctx.lineTo(rect.x + rect.width - r, rect.y)\n ctx.arcTo(rect.x + rect.width, rect.y, rect.x + rect.width, rect.y + r, r)\n ctx.lineTo(rect.x + rect.width, rect.y + rect.height - r)\n ctx.arcTo(rect.x + rect.width, rect.y + rect.height, rect.x + rect.width - r, rect.y + rect.height, r)\n ctx.lineTo(rect.x + r, rect.y + rect.height)\n ctx.arcTo(rect.x, rect.y + rect.height, rect.x, rect.y + rect.height - r, r)\n ctx.lineTo(rect.x, rect.y + r)\n ctx.arcTo(rect.x, rect.y, rect.x + r, rect.y, r)\n ctx.closePath()\n}\n\n/**\n * Build a painter that draws into a canvas.\n *\n * One transform carries pan and zoom; everything after that is drawn in the\n * layout's own coordinates. Coordinates are deliberately **not** snapped to\n * pixels: draw.io rounds after its transform because its canvas is 1:1 screen\n * space, while ours is scaled, so snapping would have to happen per point in\n * screen space. Antialiasing is the honest default until a measurement says the\n * blur costs readability.\n * @param canvas - the canvas to draw into.\n * @returns the painter.\n */\nexport function createCanvasPainter(canvas: HTMLCanvasElement): CardgraphPainter {\n const ctx = canvas.getContext('2d') as unknown as PaintContext | null\n if (ctx === null) throw new Error('card canvas: no 2d context')\n let width = 0\n let height = 0\n let dpr = 1\n /** Measured text widths, keyed by font and text: measuring is a layout call. */\n const widths = new Map<string, number>()\n\n const measure = (text: string): number => {\n const key = `${ctx.font}\\u0000${text}`\n const hit = widths.get(key)\n if (hit !== undefined) return hit\n const value = ctx.measureText(text).width\n widths.set(key, value)\n return value\n }\n\n const line = (x1: number, y1: number, x2: number, y2: number, colour: string, dash: readonly number[], alpha: number): void => {\n ctx.globalAlpha = alpha\n ctx.strokeStyle = colour\n ctx.setLineDash(dash)\n ctx.beginPath()\n ctx.moveTo(x1, y1)\n ctx.lineTo(x2, y2)\n ctx.stroke()\n }\n\n /** The frame's outline alone, for the interaction layer: the box is already drawn. */\n const drawFrameOutline = (frame: CardgraphFrame, scene: PaintScene): void => {\n ctx.globalAlpha = 1\n pathRoundRect(ctx, frame.bounds, FRAME_RADIUS)\n ctx.strokeStyle = scene.theme.accent\n ctx.lineWidth = 1.6\n ctx.stroke()\n }\n\n const drawFrame = (frame: CardgraphFrame, scene: PaintScene): void => {\n const { theme } = scene\n ctx.globalAlpha = 1\n pathRoundRect(ctx, frame.bounds, FRAME_RADIUS)\n ctx.fillStyle = theme.frame\n ctx.fill()\n ctx.strokeStyle = theme.frameBorder\n ctx.lineWidth = 1\n ctx.stroke()\n\n // Title bar: a band across the top, clipped to the frame so its square corners\n // do not cut across the rounded ones.\n const bar: Rect = { x: frame.bounds.x, y: frame.bounds.y, width: frame.bounds.width, height: 26 }\n ctx.save()\n pathRoundRect(ctx, frame.bounds, FRAME_RADIUS)\n ctx.clip()\n ctx.globalAlpha = 1\n ctx.fillStyle = theme.frameBar\n ctx.fillRect(bar.x, bar.y, bar.width, bar.height)\n ctx.restore()\n line(bar.x, bar.y + bar.height, bar.x + bar.width, bar.y + bar.height, theme.frameBorder, [], 1)\n\n ctx.font = fontString(theme.fonts.title)\n ctx.textAlign = 'left'\n ctx.textBaseline = 'middle'\n ctx.fillStyle = theme.secondary\n const label = frame.external ? scene.labels.external : frame.title ?? frame.shortId\n const dot = 10\n ctx.fillText(elide(label, frame.bounds.width - 24 - dot, measure), bar.x + 10, bar.y + bar.height / 2 + 0.5)\n\n // One dot for a resident session, an outline for one that is not: the same\n // distinction the list's StateDot makes.\n const cx = bar.x + bar.width - 13\n const cy = bar.y + bar.height / 2\n ctx.beginPath()\n ctx.arc(cx, cy, 3.5, 0, Math.PI * 2)\n if (frame.live) {\n ctx.fillStyle = theme.success\n ctx.fill()\n } else {\n ctx.strokeStyle = theme.tertiary\n ctx.lineWidth = 1\n ctx.stroke()\n }\n\n if (frame.cards === 0) {\n ctx.font = fontString(theme.fonts.body)\n ctx.fillStyle = theme.tertiary\n ctx.fillText(scene.labels.empty, frame.bounds.x + CARD_PADDING, bar.y + bar.height + 18)\n }\n }\n\n const drawWire = (edge: CardgraphEdge, scene: PaintScene, alpha: number): void => {\n const { theme } = scene\n const style = wireStyle(edge.kind, theme)\n const points = wirePoints(edge)\n ctx.globalAlpha = alpha\n ctx.strokeStyle = style.stroke\n ctx.lineWidth = style.width\n ctx.setLineDash(style.dash)\n ctx.beginPath()\n points.forEach((point, index) => {\n if (index === 0) ctx.moveTo(point.x, point.y)\n else ctx.lineTo(point.x, point.y)\n })\n ctx.stroke()\n\n // The head turns with the last segment, so a wire that arrives through a\n // gutter points into the frame rather than along the way it came.\n const last = points[points.length - 2]\n const end = points[points.length - 1]\n if (last === undefined || end === undefined) return\n const angle = Math.atan2(end.y - last.y, end.x - last.x)\n const back = { x: end.x - Math.cos(angle) * ARROW_LENGTH, y: end.y - Math.sin(angle) * ARROW_LENGTH }\n const side = { x: -Math.sin(angle) * ARROW_HALF, y: Math.cos(angle) * ARROW_HALF }\n ctx.setLineDash([])\n ctx.fillStyle = style.stroke\n ctx.beginPath()\n ctx.moveTo(end.x, end.y)\n ctx.lineTo(back.x + side.x, back.y + side.y)\n ctx.lineTo(back.x - side.x, back.y - side.y)\n ctx.closePath()\n ctx.fill()\n }\n\n const drawCard = (card: CardgraphCard, scene: PaintScene, accent: boolean): void => {\n const { theme } = scene\n const box = card.bounds\n ctx.globalAlpha = 1\n pathRoundRect(ctx, box, CARD_RADIUS)\n ctx.fillStyle = card.lod === 'chip' ? statusColour(card.status, theme) : theme.surface\n ctx.fill()\n if (card.lod === 'chip') return\n\n ctx.strokeStyle = accent ? theme.accent : theme.border\n ctx.lineWidth = accent ? 1.6 : 1\n ctx.stroke()\n\n // Status stripe, in the tone the tab's Tag uses for the same status.\n ctx.save()\n pathRoundRect(ctx, box, CARD_RADIUS)\n ctx.clip()\n ctx.fillStyle = statusColour(card.status, theme)\n ctx.fillRect(box.x, box.y, STRIPE, box.height)\n ctx.restore()\n\n const textLeft = box.x + CARD_PADDING + STRIPE\n const textWidth = box.width - CARD_PADDING * 2 - STRIPE\n ctx.textAlign = 'left'\n ctx.textBaseline = 'top'\n\n ctx.font = fontString(theme.fonts.id)\n ctx.fillStyle = accent ? theme.accent : theme.secondary\n if (card.lod === 'compact') {\n // One line: the id, then as much subject as is left of the card.\n const id = card.report\n const idWidth = measure(id)\n const rest = foldText(`· ${card.subject}`, textWidth - idWidth - 4, measure, 1)[0] ?? ''\n ctx.fillText(id, textLeft, box.y + box.height / 2 - theme.fonts.id.size / 2 - 1)\n ctx.font = fontString(theme.fonts.body)\n ctx.fillStyle = theme.caption\n ctx.fillText(rest, textLeft + idWidth + 4, box.y + box.height / 2 - theme.fonts.body.size / 2 - 1)\n return\n }\n\n ctx.fillText(card.report, textLeft, box.y + 8)\n ctx.font = fontString(theme.fonts.body)\n ctx.fillStyle = theme.text\n const lines = foldText(card.subject, textWidth, measure, SUBJECT_LINES)\n lines.forEach((text, index) => {\n ctx.fillText(text, textLeft, box.y + 28 + index * theme.fonts.body.lineHeight)\n })\n\n // Footer: the status, and the task chip when there is room for both.\n ctx.font = fontString(theme.fonts.meta)\n const baseline = box.y + box.height - theme.fonts.meta.lineHeight - 6\n ctx.fillStyle = statusColour(card.status, theme)\n const status = scene.labels.status[card.status] ?? card.status\n ctx.fillText(status, textLeft, baseline)\n if (card.task !== undefined) {\n const statusWidth = measure(status)\n const chipLeft = textLeft + statusWidth + 8\n const chipText = foldText(card.task, box.x + box.width - CARD_PADDING - chipLeft - 10, measure, 1)[0] ?? ''\n if (chipText !== '') {\n ctx.fillStyle = theme.frameBar\n const chipWidth = measure(chipText) + 10\n ctx.fillRect(chipLeft, baseline - 2, chipWidth, theme.fonts.meta.lineHeight + 2)\n ctx.fillStyle = theme.caption\n ctx.fillText(chipText, chipLeft + 5, baseline)\n }\n }\n }\n\n return {\n resize(w: number, h: number, ratio: number): void {\n width = Math.max(0, w)\n height = Math.max(0, h)\n dpr = Number.isFinite(ratio) && ratio > 0 ? ratio : 1\n canvas.width = Math.max(1, Math.round(width * dpr))\n canvas.height = Math.max(1, Math.round(height * dpr))\n canvas.style.width = `${width}px`\n canvas.style.height = `${height}px`\n },\n reset(): void {\n widths.clear()\n },\n paint(scene: PaintScene): void {\n const { layout, theme, viewport } = scene\n ctx.setTransform(dpr, 0, 0, dpr, 0, 0)\n ctx.globalAlpha = 1\n ctx.setLineDash([])\n ctx.clearRect(0, 0, width, height)\n ctx.fillStyle = theme.canvas\n ctx.fillRect(0, 0, width, height)\n\n const world = worldViewport(viewport, scene.overscan ?? 0)\n const visible = (rect: Rect): boolean => intersects(rect, world)\n\n ctx.save()\n ctx.translate(viewport.offsetX, viewport.offsetY)\n ctx.scale(viewport.scale, viewport.scale)\n\n // Frames first, then wires, then cards. A wire therefore always reads on top\n // of the boxes it travels between, while a card's text is never crossed.\n for (const frame of layout.frames) {\n if (visible(frame.bounds)) drawFrame(frame, scene)\n }\n for (const edge of layout.edges) {\n if (visible(edge.bounds)) drawWire(edge, scene, 1)\n }\n for (const card of layout.cards) {\n if (!visible(card.bounds)) continue\n drawCard(card, scene, card.report === scene.focus?.selected)\n }\n ctx.globalAlpha = 1\n ctx.restore()\n },\n paintPath(scene: PaintScene): void {\n const { layout, theme, viewport } = scene\n const report = scene.focus?.report ?? scene.focus?.selected\n const frameIndex = scene.focus?.frame\n ctx.setTransform(dpr, 0, 0, dpr, 0, 0)\n ctx.globalAlpha = 1\n ctx.setLineDash([])\n ctx.clearRect(0, 0, width, height)\n if (report === undefined && frameIndex === undefined) return\n\n ctx.save()\n ctx.translate(viewport.offsetX, viewport.offsetY)\n ctx.scale(viewport.scale, viewport.scale)\n\n if (report === undefined) {\n const frame = layout.frames[frameIndex as number]\n if (frame !== undefined) drawFrameOutline(frame, scene)\n ctx.restore()\n return\n }\n\n // The scrim: one rectangle over the whole visible world. Everything else\n // recedes at once, and — unlike dimming shapes one by one — it costs the same\n // whether the drawing has ten reports or three thousand.\n const world = worldViewport(viewport, 0)\n ctx.globalAlpha = SCRIM_ALPHA\n ctx.fillStyle = theme.canvas\n ctx.fillRect(world.x, world.y, world.width, world.height)\n ctx.globalAlpha = 1\n\n const edges = layout.edges.filter((edge) => edge.report === report)\n const touched = new Set<number>()\n for (const edge of edges) {\n if (edge.from.kind === 'frame') touched.add(edge.from.index)\n if (edge.to.kind === 'frame') touched.add(edge.to.index)\n }\n // The frames the path touches are re-outlined rather than redrawn: their\n // boxes are already underneath, and only the endpoints need to stay legible.\n for (const frame of layout.frames) {\n if (touched.has(frame.index)) drawFrameOutline(frame, scene)\n }\n for (const edge of edges) {\n if (intersects(edge.bounds, world)) drawWire(edge, scene, 1)\n }\n for (const card of layout.cards) {\n if (card.report === report) drawCard(card, scene, true)\n }\n ctx.restore()\n },\n }\n}\n","/**\n * Dictionary for the report timeline tab.\n *\n * Both shipped locales are required by the client locale service, and the key\n * union drives the typed `t()` the view uses, so a missing translation is a\n * type error rather than an empty label.\n *\n * @module dsh-report-ledger/client/locales\n */\n\nimport type { HopAction, LeaseState, ReportStatus } from '../shared/wire.ts'\nimport type { TopologyEdgeKind } from './topology-model.ts'\n\n/** Chinese dictionary — the source of truth for the key set. */\nexport const zh = {\n 'view.tab': '汇报',\n 'view.loading': '正在读取账本…',\n 'view.summary': '{sessions} 个会话 · {reports} 份汇报',\n 'view.empty': '这个会话树下还没有汇报。代理用 report_author 开一份汇报后,这里会出现它的缩略卡。',\n 'view.refresh': '刷新',\n 'view.retry': '重试',\n 'view.error': '读取失败:{code}',\n 'view.root': '本会话',\n 'view.live': '在线',\n 'view.delegated': '子代理',\n 'view.detail.loading': '正在读取传递路径…',\n 'view.detail.route': '传递路径',\n 'view.detail.routeEmpty': '(还没有记录跳)',\n 'view.detail.pending': '仍在等待送达:{targets}',\n 'view.detail.body': '正文',\n 'view.detail.path': '账本文件',\n 'view.copyPath': '复制账本路径',\n 'row.hops': '{count} 跳',\n 'row.authors': '{count} 位共写',\n 'row.filterByTask': '只看任务「{task}」',\n 'row.filterByWorktree': '只看检出「{worktree}」',\n 'row.leaseHeld': '工作区:正在被这份汇报占用',\n 'row.leaseReleased': '工作区:占用已结束,检出仍留在磁盘上',\n 'row.leaseReclaimed': '工作区:已被账本回收清除',\n 'row.leaseNone': '工作区:绑定已记录,但从未被占用',\n 'row.expand': '展开 {report}',\n 'row.collapse': '收起 {report}',\n 'row.last': '最后',\n 'hop.authored': '发起',\n 'hop.contributed': '共写',\n 'hop.sent': '主送',\n 'hop.delivered': '送达',\n 'hop.cc': '抄送',\n 'hop.forwarded': '转呈',\n 'hop.copied': '副本',\n 'hop.read': '阅读',\n 'hop.acked': '回执',\n 'hop.leased': '认领工作区',\n 'hop.released': '释放工作区',\n 'hop.reclaimed': '回收工作区',\n 'hop.amended': '修正',\n 'hop.closed': '结案',\n 'hop.reopened': '重开',\n 'filter.all': '全部',\n 'filter.open': '进行中',\n 'filter.acked': '已回执',\n 'filter.closed': '已结案',\n 'filter.search': '搜索主题、编号、会话或收发…',\n 'filter.clear': '清除',\n 'filter.showing': '显示 {visible}/{total} 份汇报',\n 'filter.none': '没有符合当前筛选的汇报。',\n 'filter.hint': '状态筛选只作用于汇报;搜索会同时匹配会话标题与编号,以及任务标签和检出名。',\n 'basis.created': '按创建',\n 'basis.updated': '按最近活动',\n 'basis.hint': '汇报按创建时间还是按最后一次活动排入时间线;会话始终按创建时间。',\n 'detail.thread': '线程',\n 'detail.parent': '上溯',\n 'detail.children': '下递',\n 'detail.openLinked': '打开 {report}',\n 'detail.outside': '{report} 不在当前会话树的列表里(它可能属于另一条线);正文与路径仍可读取。',\n 'detail.filteredOut': '{report} 在当前筛选下被隐藏了(用「清除」恢复)。',\n 'detail.backToList': '回到列表',\n 'detail.bodyTruncated': '正文过长,此处只显示开头 {chars} 字;完整内容见账本文件。',\n 'topology.title': '拓扑',\n 'topology.summary': '{lanes} 条泳道 · {reports} 份汇报',\n 'topology.show': '展开拓扑',\n 'topology.hide': '收起拓扑',\n 'topology.external': '树外',\n 'topology.externalHint': '不属于本树的会话',\n 'topology.openHint': '点击在列表中打开',\n 'cardgraph.zoomIn': '放大',\n 'cardgraph.zoomOut': '缩小',\n 'cardgraph.zoom': '{percent}%',\n 'cardgraph.fit': '适应窗口',\n 'cardgraph.summary': '{frames} 个框 · {cards} 份汇报',\n 'cardgraph.empty': '没有汇报',\n 'body.copy': '复制',\n 'body.copied': '已复制',\n 'body.footnotes': '脚注',\n} as const\n\n/** English dictionary, checked against the Chinese key set. */\nexport const en: Record<keyof typeof zh, string> = {\n 'view.tab': 'Reports',\n 'view.loading': 'Reading the ledger…',\n 'view.summary': '{sessions} sessions · {reports} reports',\n 'view.empty': 'No reports in this session tree yet. Once an agent opens one with report_author, its digest card appears here.',\n 'view.refresh': 'Refresh',\n 'view.retry': 'Retry',\n 'view.error': 'Read failed: {code}',\n 'view.root': 'this session',\n 'view.live': 'live',\n 'view.delegated': 'subagent',\n 'view.detail.loading': 'Reading the transfer path…',\n 'view.detail.route': 'Transfer path',\n 'view.detail.routeEmpty': '(no hops recorded yet)',\n 'view.detail.pending': 'Still owed to: {targets}',\n 'view.detail.body': 'Body',\n 'view.detail.path': 'Ledger file',\n 'view.copyPath': 'Copy the ledger path',\n 'row.hops': '{count} hops',\n 'row.authors': '{count} authors',\n 'row.filterByTask': 'Show only task \"{task}\"',\n 'row.filterByWorktree': 'Show only checkout \"{worktree}\"',\n 'row.leaseHeld': 'Checkout: currently claimed by this report',\n 'row.leaseReleased': 'Checkout: claim ended, the checkout is still on disk',\n 'row.leaseReclaimed': 'Checkout: cleared away by the ledger',\n 'row.leaseNone': 'Checkout: bound but never claimed',\n 'row.expand': 'Expand {report}',\n 'row.collapse': 'Collapse {report}',\n 'row.last': 'last',\n 'hop.authored': 'authored',\n 'hop.contributed': 'contributed',\n 'hop.sent': 'sent',\n 'hop.delivered': 'delivered',\n 'hop.cc': 'cc',\n 'hop.forwarded': 'forwarded',\n 'hop.copied': 'copied',\n 'hop.read': 'read',\n 'hop.acked': 'acked',\n 'hop.leased': 'leased',\n 'hop.released': 'released',\n 'hop.reclaimed': 'reclaimed',\n 'hop.amended': 'amended',\n 'hop.closed': 'closed',\n 'hop.reopened': 'reopened',\n 'filter.all': 'All',\n 'filter.open': 'Open',\n 'filter.acked': 'Acked',\n 'filter.closed': 'Closed',\n 'filter.search': 'Search subject, id, session, or people…',\n 'filter.clear': 'Clear',\n 'filter.showing': 'Showing {visible}/{total} reports',\n 'filter.none': 'No reports match the current filter.',\n 'filter.hint': 'The status filter narrows reports only; search matches every row, including session titles and ids, task labels and checkout names.',\n 'basis.created': 'By creation',\n 'basis.updated': 'By activity',\n 'basis.hint': 'Whether reports are placed at creation or at their latest activity; sessions always keep their creation time.',\n 'detail.thread': 'Thread',\n 'detail.parent': 'Answers',\n 'detail.children': 'Answered by',\n 'detail.openLinked': 'Open {report}',\n 'detail.outside': '{report} is not in this session tree\\'s list (it may belong to another line); its body and path are still readable.',\n 'detail.filteredOut': '{report} is hidden by the current filter (use Clear to restore it).',\n 'detail.backToList': 'Back to the list',\n 'detail.bodyTruncated': 'Body is long, so only the first {chars} characters are shown here; the ledger file has the whole thing.',\n 'topology.title': 'Topology',\n 'topology.summary': '{lanes} lanes · {reports} reports',\n 'topology.show': 'Show topology',\n 'topology.hide': 'Hide topology',\n 'topology.external': 'outside',\n 'topology.externalHint': 'Sessions outside this tree',\n 'topology.openHint': 'Click to open it in the list',\n 'cardgraph.zoomIn': 'Zoom in',\n 'cardgraph.zoomOut': 'Zoom out',\n 'cardgraph.zoom': '{percent}%',\n 'cardgraph.fit': 'Fit',\n 'cardgraph.summary': '{frames} frames · {cards} reports',\n 'cardgraph.empty': 'no reports',\n 'body.copy': 'Copy',\n 'body.copied': 'Copied',\n 'body.footnotes': 'Footnotes',\n}\n\n/** Every key the view may translate. */\nexport type ReportLedgerKey = keyof typeof zh\n\n/**\n * Lifecycle state → dictionary key.\n *\n * Deliberately the SAME keys the toolbar's filter chips use. A card's status\n * chip and the filter that selects it name one state, so giving them two\n * vocabularies (raw `open` on the card, 进行中 in the toolbar) made the reader\n * build the mapping themselves — and the mapping was the bug, not the words.\n */\nexport const STATUS_LABEL: Record<ReportStatus, ReportLedgerKey> = {\n open: 'filter.open',\n acked: 'filter.acked',\n closed: 'filter.closed',\n}\n\n/**\n * Lifecycle state → the shell's chip tone.\n *\n * Colour-coded status is the point of the chip, and the shell's palette already\n * has the three tones this needs — so the states ride `Tag` instead of a\n * hand-mixed background. `open` reads as the one that wants attention. The tone\n * names are the ones the shell's CSS actually defines (`[data-tone=…]`), which\n * is a longer list than the shipped views happen to use.\n */\nexport const STATUS_TONE: Record<ReportStatus, 'warning' | 'success' | 'neutral'> = {\n open: 'warning',\n acked: 'success',\n closed: 'neutral',\n}\n\n/** Lifecycle state → the shell's state dot. `ongoing` is the animated one. */\nexport const STATUS_DOT: Record<ReportStatus, 'ongoing' | 'done' | 'idle'> = {\n open: 'ongoing',\n acked: 'done',\n closed: 'idle',\n}\n\n/**\n * Edge kind → the word the drawing calls it.\n *\n * The topology borrows the transfer path's own vocabulary rather than inventing\n * a second one: an arrow in the graph and a row in the panel name the same\n * relation, so they say the same thing.\n */\nexport const EDGE_LABEL: Record<TopologyEdgeKind, ReportLedgerKey> = {\n to: 'hop.sent',\n cc: 'hop.cc',\n author: 'hop.contributed',\n thread: 'detail.thread',\n}\n\n/**\n * Transfer-path hop action → dictionary key.\n *\n * These are ledger vocabulary: the model and the ledger files keep the raw\n * tokens (`authored`, `delivered`, …), and only the human view speaks a\n * language. Keyed by the full action union so a new hop action cannot ship\n * without a word for it.\n */\n/** Lease-state description, for the checkout chip's tooltip. */\nexport const LEASE_HINT: Record<LeaseState, ReportLedgerKey> = {\n held: 'row.leaseHeld',\n released: 'row.leaseReleased',\n reclaimed: 'row.leaseReclaimed',\n none: 'row.leaseNone',\n}\n\n/** Hop action label, for the transfer-path rendering. */\nexport const HOP_LABEL: Record<HopAction, ReportLedgerKey> = {\n authored: 'hop.authored',\n contributed: 'hop.contributed',\n sent: 'hop.sent',\n delivered: 'hop.delivered',\n cc: 'hop.cc',\n forwarded: 'hop.forwarded',\n copied: 'hop.copied',\n read: 'hop.read',\n acked: 'hop.acked',\n leased: 'hop.leased',\n released: 'hop.released',\n reclaimed: 'hop.reclaimed',\n amended: 'hop.amended',\n closed: 'hop.closed',\n reopened: 'hop.reopened',\n}\n","/**\n * The card canvas's view: two canvases, a pan/zoom transform, picking, and an\n * invisible button layer.\n *\n * The drawing is split across two surfaces, and that split is the whole\n * performance story:\n *\n * - the **base** canvas holds frames, wires and cards, and is repainted only when\n * the drawing itself changes (layout, transform, size, theme);\n * - the **interaction** canvas holds one report's path over a scrim, and is\n * repainted on every hover. Its cost does not grow with the ledger, because it\n * never touches the shapes underneath.\n *\n * Picking is arithmetic rather than elements: the canvas has nothing to hit, so\n * `hitTest` from the model is given the cards (topmost first) and then the frames.\n * What the canvas also cannot offer is accessibility — so every card that is on\n * screen gets a real, transparent `<button>` on top of it, which is what a keyboard\n * or a screen reader reaches, and the list below stays the full accessible path.\n *\n * @module dsh-report-ledger/client/CardgraphView\n */\n\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport type { PointerEvent as ReactPointerEvent, ReactElement } from 'react'\nimport { Button } from '@deepseek-ai/dsh-client-ui-primitives'\nimport type { ReportFrontMatter, TimelinePayload } from '../shared/wire.ts'\nimport { buildCardgraph, hitTest, lodFor } from './cardgraph-model.ts'\nimport type { CardgraphLayout, CardgraphViewport, Rect } from './cardgraph-model.ts'\nimport {\n FALLBACK_THEME, WIRE_TOKENS, createCanvasPainter, createThemeReader,\n} from './cardgraph-painter.ts'\nimport type { CardgraphPainter, ThemeReader } from './cardgraph-painter.ts'\nimport { EDGE_LABEL, STATUS_LABEL } from './locales.ts'\nimport type { Translate } from './ReportsView.tsx'\n\n/** How far the reader may zoom out and in. */\nconst MIN_ZOOM = 0.25\nconst MAX_ZOOM = 2.5\n/** One wheel notch. */\nconst ZOOM_STEP = 1.25\n/** Drawing units to keep beyond the viewport, so a pan does not reveal a blank band. */\nconst OVERSCAN = 160\n/** How tall the canvas box is, inside the tab. */\nconst VIEW_HEIGHT = 420\n/** How far the pointer may travel before a press becomes a drag instead of a click. */\nconst DRAG_SLOP = 4\n/** The four kinds, in the order the legend lists them. */\nconst KINDS = ['to', 'cc', 'author', 'thread'] as const\n\n/** One pickable thing, shaped for the model's `hitTest`. */\ninterface PickTarget {\n readonly kind: 'card' | 'frame'\n readonly report?: string\n readonly frame?: number\n readonly bounds: Rect\n}\n\n/** Props for the card canvas. */\nexport interface CardgraphViewProps {\n /** The endpoint payload, for the session frames. */\n readonly payload: TimelinePayload\n /** The digests being plotted — the same filtered set the list renders. */\n readonly reports: readonly ReportFrontMatter[]\n readonly t: Translate\n /** The report whose card is open, if any. */\n readonly openReport?: string\n /** Open a report's card in the list below. */\n readonly onOpen: (report: string) => void\n /** Full id plus session title, for the frame's hover label. */\n readonly idHint: (id: string) => string\n}\n\n/**\n * The legend.\n *\n * Built from the painter's own wire table (`WIRE_TOKENS`) rather than from CSS, so\n * a legend that disagreed with the lines beside it would take a change to that one\n * table — the drift the swimlane's legend avoided a different way (by sharing a\n * class name with its paths).\n * @param props - the translator.\n * @returns the legend row.\n */\nexport function CardgraphLegend({ t }: { readonly t: Translate }): ReactElement {\n return (\n <div style={{ display: 'flex', alignItems: 'center', gap: '12px', flexWrap: 'wrap' }}>\n {KINDS.map((kind) => (\n <span\n key={kind}\n style={{ display: 'inline-flex', alignItems: 'center', gap: '6px', fontSize: 'var(--dsw-font-xxxs-11, 11px)', color: 'var(--dsw-alias-label-caption)' }}\n >\n <svg width=\"26\" height=\"8\" aria-hidden=\"true\" focusable=\"false\">\n <line\n x1=\"0\"\n y1=\"4\"\n x2=\"26\"\n y2=\"4\"\n stroke={`var(${WIRE_TOKENS[kind].colour})`}\n strokeWidth={WIRE_TOKENS[kind].width}\n {...(WIRE_TOKENS[kind].dash.length === 0 ? {} : { strokeDasharray: WIRE_TOKENS[kind].dash.join(' ') })}\n />\n </svg>\n {t(EDGE_LABEL[kind])}\n </span>\n ))}\n </div>\n )\n}\n\n/** Keep a number inside a range. */\nconst clamp = (value: number, low: number, high: number): number => Math.min(high, Math.max(low, value))\n\n/**\n * The drawing.\n * @param props - payload, the reports to plot, translations, and the open handler.\n * @returns the canvas box with its controls.\n */\nexport function CardgraphView({ payload, reports, t, openReport, onOpen, idHint }: CardgraphViewProps): ReactElement {\n const boxRef = useRef<HTMLDivElement | null>(null)\n const baseRef = useRef<HTMLCanvasElement | null>(null)\n const pathRef = useRef<HTMLCanvasElement | null>(null)\n const painter = useRef<CardgraphPainter | null>(null)\n const pathPainter = useRef<CardgraphPainter | null>(null)\n const reader = useRef<ThemeReader | null>(null)\n const drag = useRef<{ x: number; y: number; offsetX: number; offsetY: number; moved: boolean } | null>(null)\n\n const [size, setSize] = useState({ width: 0, height: VIEW_HEIGHT })\n const [zoom, setZoom] = useState(1)\n const [offset, setOffset] = useState({ x: 0, y: 0 })\n const [hover, setHover] = useState<{ report?: string; frame?: number }>({})\n const [ready, setReady] = useState(false)\n const fitted = useRef(false)\n /** The transform as of this render, for the one listener that is attached once. */\n const zoomRef = useRef(zoom)\n const offsetRef = useRef(offset)\n zoomRef.current = zoom\n offsetRef.current = offset\n\n const lod = lodFor(zoom)\n const layout = useMemo(() => buildCardgraph(payload, reports, { lod }), [payload, reports, lod])\n\n const labels = useMemo(() => {\n const status: Record<string, string> = {}\n for (const [state, key] of Object.entries(STATUS_LABEL)) status[state] = t(key)\n return { external: t('topology.external'), empty: t('cardgraph.empty'), status }\n }, [t])\n\n // ---------------------------------------------------------------------------\n // Mounting: painters, theme reader, and the observed size\n // ---------------------------------------------------------------------------\n useEffect(() => {\n const base = baseRef.current\n const path = pathRef.current\n if (base === null || path === null) return\n painter.current = createCanvasPainter(base)\n pathPainter.current = createCanvasPainter(path)\n reader.current = createThemeReader(globalThis.document.documentElement, globalThis.window)\n setReady(true)\n return () => {\n painter.current = null\n pathPainter.current = null\n reader.current = null\n setReady(false)\n }\n }, [])\n\n useEffect(() => {\n const box = boxRef.current\n if (box === null) return\n const measure = (): void => {\n setSize({ width: box.clientWidth, height: box.clientHeight })\n }\n measure()\n const observer = new ResizeObserver(measure)\n observer.observe(box)\n return () => { observer.disconnect() }\n }, [])\n\n const viewport = useMemo<CardgraphViewport>(\n () => ({ offsetX: offset.x, offsetY: offset.y, scale: zoom, width: size.width, height: size.height }),\n [offset, zoom, size],\n )\n const focus = useMemo(\n () => ({\n ...(hover.report === undefined ? {} : { report: hover.report }),\n ...(hover.frame === undefined ? {} : { frame: hover.frame }),\n ...(openReport === undefined ? {} : { selected: openReport }),\n }),\n [hover, openReport],\n )\n const scene = useMemo(() => ({\n layout,\n theme: reader.current?.read() ?? FALLBACK_THEME,\n viewport,\n focus,\n labels,\n overscan: OVERSCAN,\n }), [layout, viewport, focus, labels])\n\n // ---------------------------------------------------------------------------\n // Painting\n // ---------------------------------------------------------------------------\n useEffect(() => {\n const target = painter.current\n if (!ready || target === null) return\n target.resize(size.width, size.height, globalThis.devicePixelRatio || 1)\n // The base layer is drawn with nothing focused: focus lives on the overlay, so\n // a hover never repaints the shapes underneath it.\n target.paint({ ...scene, focus: openReport === undefined ? {} : { selected: openReport } })\n }, [ready, scene, size, openReport])\n\n useEffect(() => {\n const target = pathPainter.current\n if (!ready || target === null) return\n target.resize(size.width, size.height, globalThis.devicePixelRatio || 1)\n target.paintPath(scene)\n }, [ready, scene, size])\n\n // ---------------------------------------------------------------------------\n // Picking\n // ---------------------------------------------------------------------------\n /** Everything under the pointer, topmost first: cards, then the frames holding them. */\n const targets = useMemo<readonly PickTarget[]>(() => [\n ...[...layout.cards].reverse().map((card) => ({ kind: 'card' as const, report: card.report, bounds: card.bounds })),\n ...[...layout.frames].reverse().map((frame) => ({ kind: 'frame' as const, frame: frame.index, bounds: frame.bounds })),\n ], [layout])\n\n const pick = useCallback((clientX: number, clientY: number): PickTarget | undefined => {\n const box = boxRef.current\n if (box === null) return undefined\n const rect = box.getBoundingClientRect()\n const x = (clientX - rect.left - offset.x) / zoom\n const y = (clientY - rect.top - offset.y) / zoom\n return hitTest(targets, x, y)\n }, [targets, offset, zoom])\n\n // ---------------------------------------------------------------------------\n // Fitting, zooming and panning\n // ---------------------------------------------------------------------------\n const fit = useCallback(() => {\n const box = boxRef.current\n if (box === null || layout.width <= 0 || layout.height <= 0) return\n const width = box.clientWidth\n const height = box.clientHeight\n const next = clamp(Math.min((width - 24) / layout.width, (height - 24) / layout.height), MIN_ZOOM, 1)\n setZoom(next)\n setOffset({\n x: Math.max(8, (width - layout.width * next) / 2),\n y: Math.max(8, (height - layout.height * next) / 2),\n })\n }, [layout])\n\n /**\n * The first view, which is deliberately **not** the fit.\n *\n * Fitting the whole drawing into a 420px box on a three-column tree lands around\n * 57%, which is the compact tier — so the reader's first sight of a card canvas\n * would be a wall of one-line cards with the subjects this design is about elided.\n * Measured on the real tab, not reasoned about: the first screenshot of the wiring\n * came out at 57% and proved the point.\n *\n * So the first view fits the **width** and never drops below the card tier: frames,\n * columns and readable cards all visible at once, and the reader scrolls down\n * instead of squinting. `适应窗口` still gives the whole picture on demand.\n */\n useEffect(() => {\n if (fitted.current || size.width === 0 || layout.cards.length === 0) return\n fitted.current = true\n const next = clamp((size.width - 24) / Math.max(layout.width, 1), 0.8, 1)\n setZoom(next)\n setOffset({ x: Math.max(8, (size.width - layout.width * next) / 2), y: 12 })\n }, [layout, size])\n\n // A real, non-passive wheel listener: React's `onWheel` is passive at the root,\n // where `preventDefault` is a no-op and the page scrolls under the zoom.\n //\n // The listener is attached once, so it reads the current transform through refs\n // rather than closing over it. The obvious alternative — computing the next zoom\n // inside a `setZoom` updater and calling `setOffset` from in there — is a side\n // effect inside an updater, which React is free to invoke twice; the offset would\n // then be shifted twice per notch and the zoom would drift away from the cursor.\n useEffect(() => {\n const box = boxRef.current\n if (box === null) return\n const onWheel = (event: WheelEvent): void => {\n event.preventDefault()\n const rect = box.getBoundingClientRect()\n const px = event.clientX - rect.left\n const py = event.clientY - rect.top\n const current = zoomRef.current\n const next = clamp(current * (event.deltaY < 0 ? ZOOM_STEP : 1 / ZOOM_STEP), MIN_ZOOM, MAX_ZOOM)\n const ratio = next / current\n const previous = offsetRef.current\n // Keep the point under the cursor fixed: the offset scales about it.\n setOffset({ x: px - (px - previous.x) * ratio, y: py - (py - previous.y) * ratio })\n setZoom(next)\n }\n box.addEventListener('wheel', onWheel, { passive: false })\n return () => { box.removeEventListener('wheel', onWheel) }\n }, [])\n\n /** Drag to pan, click to open. Distinguishing them by distance travelled. */\n const onPointerDown = useCallback((event: ReactPointerEvent<HTMLDivElement>) => {\n if (event.button !== 0) return\n drag.current = { x: event.clientX, y: event.clientY, offsetX: offset.x, offsetY: offset.y, moved: false }\n }, [offset])\n\n const onPointerMove = useCallback((event: ReactPointerEvent<HTMLDivElement>) => {\n const pressing = drag.current\n if (pressing !== null) {\n const dx = event.clientX - pressing.x\n const dy = event.clientY - pressing.y\n if (Math.abs(dx) + Math.abs(dy) > DRAG_SLOP) pressing.moved = true\n if (pressing.moved) {\n setOffset({ x: pressing.offsetX + dx, y: pressing.offsetY + dy })\n return\n }\n }\n const hit = pick(event.clientX, event.clientY)\n setHover(hit?.kind === 'card' && hit.report !== undefined\n ? { report: hit.report }\n : hit?.kind === 'frame' && hit.frame !== undefined ? { frame: hit.frame } : {})\n }, [pick])\n\n const onPointerUp = useCallback((event: ReactPointerEvent<HTMLDivElement>) => {\n const pressing = drag.current\n drag.current = null\n if (pressing === null || pressing.moved) return\n const hit = pick(event.clientX, event.clientY)\n if (hit?.kind === 'card' && hit.report !== undefined) onOpen(hit.report)\n }, [pick, onOpen])\n\n const onPointerLeave = useCallback(() => {\n drag.current = null\n setHover({})\n }, [])\n\n // ---------------------------------------------------------------------------\n // The accessible layer: one real button per visible card\n // ---------------------------------------------------------------------------\n const visibleCards = useMemo(() => {\n const left = -offset.x / zoom\n const top = -offset.y / zoom\n const right = (size.width - offset.x) / zoom\n const bottom = (size.height - offset.y) / zoom\n return layout.cards.filter((card) => card.bounds.x < right && left < card.bounds.x + card.bounds.width\n && card.bounds.y < bottom && top < card.bounds.y + card.bounds.height)\n }, [layout, offset, zoom, size])\n\n const percent = Math.round(zoom * 100)\n return (\n <div style={{ display: 'flex', flexDirection: 'column', gap: '6px' }}>\n <div style={controls}>\n <Button variant=\"outline\" size=\"sm\" aria-label={t('cardgraph.zoomOut')} onClick={() => { setZoom((z) => clamp(z / ZOOM_STEP, MIN_ZOOM, MAX_ZOOM)) }}>−</Button>\n <span style={percentStyle}>{t('cardgraph.zoom', { percent })}</span>\n <Button variant=\"outline\" size=\"sm\" aria-label={t('cardgraph.zoomIn')} onClick={() => { setZoom((z) => clamp(z * ZOOM_STEP, MIN_ZOOM, MAX_ZOOM)) }}>+</Button>\n <Button variant=\"outline\" size=\"sm\" onClick={fit}>{t('cardgraph.fit')}</Button>\n <CardgraphLegend t={t} />\n <span style={percentStyle}>{t('cardgraph.summary', { frames: layout.frames.length, cards: layout.cards.length })}</span>\n </div>\n <div\n ref={boxRef}\n onPointerDown={onPointerDown}\n onPointerMove={onPointerMove}\n onPointerUp={onPointerUp}\n onPointerLeave={onPointerLeave}\n style={{\n ...box,\n height: `${VIEW_HEIGHT}px`,\n cursor: drag.current?.moved === true ? 'grabbing' : hover.report === undefined ? 'grab' : 'pointer',\n }}\n >\n <canvas ref={baseRef} role=\"img\" aria-label={t('cardgraph.summary', { frames: layout.frames.length, cards: layout.cards.length })} style={layer} />\n <canvas ref={pathRef} aria-hidden=\"true\" style={layer} />\n {visibleCards.map((card) => (\n <button\n key={card.report}\n type=\"button\"\n aria-label={`${card.report} · ${card.subject}`}\n title={idHint(card.report)}\n onClick={() => { onOpen(card.report) }}\n onFocus={() => { setHover({ report: card.report }) }}\n onBlur={() => { setHover({}) }}\n style={{\n position: 'absolute',\n left: `${card.bounds.x * zoom + offset.x}px`,\n top: `${card.bounds.y * zoom + offset.y}px`,\n width: `${card.bounds.width * zoom}px`,\n height: `${card.bounds.height * zoom}px`,\n padding: 0,\n border: 0,\n background: 'transparent',\n cursor: 'pointer',\n }}\n />\n ))}\n </div>\n </div>\n )\n}\n\nconst controls: Record<string, string> = {\n display: 'flex',\n alignItems: 'center',\n gap: '8px',\n flexWrap: 'wrap',\n}\n\nconst percentStyle: Record<string, string> = {\n fontSize: 'var(--dsw-font-xxxs-11, 11px)',\n color: 'var(--dsw-alias-label-caption)',\n fontVariantNumeric: 'tabular-nums',\n}\n\nconst box: Record<string, string> = {\n position: 'relative',\n overflow: 'hidden',\n borderRadius: '6px',\n border: '1px solid var(--dsw-alias-border-l1)',\n touchAction: 'none',\n}\n\nconst layer: Record<string, string> = {\n position: 'absolute',\n inset: '0',\n display: 'block',\n}\n","/**\n * The plugin's own stylesheet.\n *\n * The shell's primitives ship their own CSS with the shell, which is the whole\n * point of using them — a plugin gets the app's real look without carrying a\n * theme. What is left for us are the two things only a consumer can know:\n *\n * 1. **Layout that belongs to the consumer.** `Input` sizes itself to its\n * content (`display: inline-flex`, no width), which is right in a form and\n * wrong in this tab's toolbar, where the search seat takes the slack. The\n * component puts the `className` it is given on its own wrapper, so one rule\n * is enough — and targeting a class of our own keeps us off the hashed class\n * names, which are free to change between shell releases.\n *\n * 2. **Drawing the topology.** The graph is made of elements the shell has no\n * primitive for — edges, lane rails, row separators, the hover dimming — so\n * these rules style OUR OWN svg and divs. Nothing here restyles a shell\n * component; every library element keeps the look the shell gave it.\n *\n * Every colour comes from a `--dsw-*` token, so the drawing follows the theme\n * (light or dark) without knowing anything about it.\n *\n * @module dsh-report-ledger/client/styles\n */\n\n/** Class the tab puts on the toolbar's search seat. */\nexport const SEARCH_CLASS = 'report-ledger-search'\n\n/** Applied to everything that is not part of the hovered report's path. */\nexport const DIM_CLASS = 'report-ledger-dim'\n\n/**\n * The class names the topology draws with.\n *\n * Exported as functions rather than written out at each call site so the legend\n * cannot drift from the lines it explains: both ask for the same class, and the\n * browser resolves both to the same rule.\n * @param kind - the edge kind.\n * @returns the classes for the path.\n */\nexport function edgeClass(kind: string): string {\n return `report-ledger-edge report-ledger-edge-${kind}`\n}\n\n/**\n * The arrowhead class for one edge kind.\n * @param kind - the edge kind.\n * @returns the class for the polygon.\n */\nexport function arrowClass(kind: string): string {\n return `report-ledger-arrow-${kind}`\n}\n\n/** Element id, so a reload replaces the sheet instead of stacking one per apply. */\nconst STYLE_ID = 'dsh-report-ledger-styles'\n\n/** The adapter rules, kept in one place so the id and the text cannot drift. */\nconst CSS = `\n.${SEARCH_CLASS} { flex: 1 1 auto; min-width: 0; }\n\n/* ── topology: our own drawing primitives ──────────────────────────────────\n Rails and row separators are hairlines; edges carry the relation, so only\n they get colour and dash pattern. Everything reads through theme tokens. */\n.report-ledger-rail { stroke: var(--dsw-alias-border-l1); stroke-width: 1; }\n.report-ledger-rowsep { stroke: var(--dsw-alias-border-l1); stroke-width: 1; opacity: .45; }\n.report-ledger-headsep { stroke: var(--dsw-alias-border-l2); stroke-width: 1; }\n.report-ledger-edge { fill: none; }\n.report-ledger-edge-to { stroke: var(--dsw-alias-label-secondary); stroke-width: 1.4; }\n.report-ledger-edge-cc { stroke: var(--dsw-alias-label-caption); stroke-width: 1; stroke-dasharray: 2 3; }\n.report-ledger-edge-author { stroke: var(--dsw-alias-label-caption); stroke-width: 1; stroke-dasharray: 1 3; }\n.report-ledger-edge-thread { stroke: var(--dsw-alias-state-business-label, var(--dsw-alias-label-secondary)); stroke-width: 1.2; }\n.report-ledger-arrow-to { fill: var(--dsw-alias-label-secondary); }\n.report-ledger-arrow-cc { fill: var(--dsw-alias-label-caption); }\n.report-ledger-arrow-author { fill: var(--dsw-alias-label-caption); }\n.report-ledger-arrow-thread { fill: var(--dsw-alias-state-business-label, var(--dsw-alias-label-secondary)); }\n/* A hover label over a line needs its own backing, or the line runs through the word. */\n.report-ledger-edge-label {\n padding: 0 4px;\n border-radius: 3px;\n font-size: var(--dsw-font-xxxs-11, 11px);\n line-height: 15px;\n white-space: nowrap;\n color: var(--dsw-alias-label-secondary);\n background: var(--dsw-alias-bg-base);\n}\n/* ── hover focus ───────────────────────────────────────────────────────────\n The dim goes on ONE element — the edge layer — and never through a descendant\n selector. Measured on a 300-report drawing: flipping a data attribute that a\n descendant rule matches costs ~35ms per hover on its own, because the browser\n recalculates style for the whole subtree whether or not the result changes\n anything visible; dimming 831 individual groups cost ~89ms. One class on one\n layer costs neither.\n\n Nodes are deliberately NOT dimmed: while tracing one report's path it is the\n *relations* that need to recede, not the participants. */\n.${DIM_CLASS} { opacity: .18; }\n`\n\n/**\n * Mount the adapter stylesheet.\n * @returns a disposer that removes it, owned by the plugin's effect scope.\n */\nexport function installStyles(): () => void {\n const doc = globalThis.document\n if (doc === undefined) return () => {}\n // A reload of the plugin re-runs apply without a page reload, so replace any\n // sheet a previous generation left behind rather than stacking duplicates.\n doc.getElementById(STYLE_ID)?.remove()\n const style = doc.createElement('style')\n style.id = STYLE_ID\n style.textContent = CSS\n doc.head.append(style)\n return () => { style.remove() }\n}\n","/**\n * The report timeline view — the `conversation.view` tab this plugin registers.\n *\n * It renders one top-to-bottom timeline of the collaboration: the session\n * subtree as it branched, interleaved with the reports those sessions produced\n * or were copied on. Rows carry only the digest, so a long-running tree stays\n * scannable; clicking a report opens its transfer path and body on demand.\n *\n * Data comes from this plugin's own read-only endpoint rather than a generated\n * remote: the host assembles the subtree and filters the ledger to the sessions\n * in it, which the browser cannot do on its own because a report's transfer path\n * is a host-side file, not session state.\n *\n * A blank session never reaches this component — the conversation slot is\n * omitted entirely in that state.\n *\n * @module dsh-report-ledger/client/ReportsView\n */\n\nimport { Fragment, useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport type { ReactElement } from 'react'\nimport {\n Button,\n IconCheckOutlineRegular,\n IconChevronDownOutlineRegular,\n IconChevronRightOutlineRegular,\n IconCopyOutlineRegular,\n IconRefreshOutlineRegular,\n Input,\n MarkdownText,\n Pill,\n StateDot,\n Tag,\n Tooltip,\n writeClipboard,\n} from '@deepseek-ai/dsh-client-ui-primitives'\nimport type { ReportDetail, TimelinePayload, WorktreeLane } from '../shared/wire.ts'\nimport {\n buildRows,\n closeOpenFence,\n displayBody,\n emptyState,\n filterRows,\n isFiltering,\n laneIndex,\n LEASE_TONE,\n reportIds,\n sessionTitles,\n shortId,\n statusCounts,\n visibleReports,\n withSynthesizedRow,\n worktreeLabel,\n type ReportTimeBasis,\n type Row,\n type StatusFilter,\n} from './timeline-model.ts'\nimport { CardgraphView } from './CardgraphView.tsx'\nimport { HOP_LABEL, LEASE_HINT, STATUS_DOT, STATUS_LABEL, STATUS_TONE, type ReportLedgerKey } from './locales.ts'\nimport { SEARCH_CLASS } from './styles.ts'\n\n/** Translate one key, substituting `{name}` placeholders. */\nexport type Translate = (key: ReportLedgerKey, params?: Record<string, string | number>) => string\n\n/** Props the registration passes through, plus the locale seat. */\nexport interface ReportsViewProps {\n /** The session the view is bound to. */\n sessionId?: string\n /** Injected by the registration closure from the plugin's locale namespace. */\n t: Translate\n}\n\nconst TIMELINE_URL = '/api/report-ledger/timeline'\nconst REPORT_URL = '/api/report-ledger/report'\n\n/**\n * How often the tab re-reads the timeline while it is on screen.\n *\n * The ledger is written by other agents in the background, so a view that loads\n * once goes stale precisely when it matters — the human is watching for a report\n * that has not been written yet. Ten seconds is quiet enough not to matter on a\n * large ledger and still fast enough to watch a collaboration happen.\n */\nconst POLL_INTERVAL_MS = 10_000\n\n/** Read one endpoint and unwrap its result envelope. */\nasync function getJson<T>(url: string): Promise<T> {\n const response = await fetch(url, { credentials: 'same-origin', headers: { accept: 'application/json' } })\n let body: unknown\n try {\n body = await response.json()\n } catch {\n throw new Error(`HTTP ${response.status}`)\n }\n const envelope = body as { ok?: boolean; code?: string; value?: T }\n if (envelope.ok !== true) throw new Error(envelope.code ?? `HTTP ${response.status}`)\n return envelope.value as T\n}\n\n/** Substitute `{name}` placeholders. */\nfunction fill(template: string, params: Record<string, string | number>): string {\n return template.replace(/\\{(\\w+)\\}/g, (whole, name: string) =>\n Object.prototype.hasOwnProperty.call(params, name) ? String(params[name]) : whole)\n}\n\n/** Format one timestamp for a timeline row. */\nfunction stamp(at: number | undefined): string {\n if (at === undefined || at <= 0) return ''\n return new Date(at).toLocaleString()\n}\n\n/** Dom id of one report's card, so the topology can scroll to it. */\nfunction rowDomId(report: string): string {\n return `report-ledger-row-${report}`\n}\n\n/**\n * How long the copy control keeps its \"copied\" label.\n *\n * The chat view uses the same one-second acknowledgement through the same\n * `writeClipboard`, so a copy here feels like a copy there.\n */\nconst COPIED_HOLD_MS = 1000\n\n/** Storage key for the remembered status filter. */\nconst FILTER_KEY = 'dsh.reportLedger.filter.v1'\n\n/** Read the remembered status filter, tolerating absent or damaged storage. */\nfunction rememberedStatus(): StatusFilter {\n try {\n const raw = globalThis.localStorage?.getItem(FILTER_KEY)\n if (typeof raw === 'string') {\n const parsed = JSON.parse(raw) as { status?: unknown }\n if (parsed.status === 'open' || parsed.status === 'acked' || parsed.status === 'closed') return parsed.status\n }\n } catch {\n // A restricted or full storage just means the filter is not remembered.\n }\n return 'all'\n}\n\n/**\n * Render the report timeline.\n * @param props - the session binding and the locale seat.\n * @returns the tab content.\n */\nexport function ReportsView(props: ReportsViewProps): ReactElement {\n const { sessionId, t } = props\n const [payload, setPayload] = useState<TimelinePayload | undefined>(undefined)\n const [error, setError] = useState<string | undefined>(undefined)\n const [timelineNonce, setTimelineNonce] = useState(0)\n // Refreshing the timeline and re-reading the open detail are two different\n // things: the background poll must move the first without touching the second,\n // or the panel would blink back to \"reading the transfer path\" every interval\n // while somebody is reading it. Only the explicit refresh button advances both.\n const [detailNonce, setDetailNonce] = useState(0)\n const [openReport, setOpenReport] = useState<string | undefined>(undefined)\n const [detail, setDetail] = useState<ReportDetail | undefined>(undefined)\n const [detailError, setDetailError] = useState<string | undefined>(undefined)\n const [status, setStatus] = useState<StatusFilter>(rememberedStatus)\n const [text, setText] = useState('')\n const [basis, setBasis] = useState<ReportTimeBasis>('created')\n const [copiedPath, setCopiedPath] = useState(false)\n const copyTimer = useRef<ReturnType<typeof globalThis.setTimeout> | undefined>(undefined)\n // The topology is an overview over the same data, so it is expanded by default\n // and collapses rather than being a separate mode: the list stays the place\n // where a report is read, and the graph stays above it.\n const [showTopology, setShowTopology] = useState(true)\n /** Set when the topology asks for a card, so the list can scroll to it once. */\n const scrollTo = useRef<string | undefined>(undefined)\n\n useEffect(() => () => {\n if (copyTimer.current !== undefined) globalThis.clearTimeout(copyTimer.current)\n }, [])\n\n useEffect(() => {\n try {\n globalThis.localStorage?.setItem(FILTER_KEY, JSON.stringify({ status }))\n } catch {\n // Remembering the filter is a convenience, never a requirement.\n }\n }, [status])\n\n useEffect(() => {\n if (sessionId === undefined || sessionId === '') {\n setError('no-session')\n return\n }\n let cancelled = false\n setError(undefined)\n getJson<TimelinePayload>(`${TIMELINE_URL}?root=${encodeURIComponent(sessionId)}`)\n .then((value) => { if (!cancelled) setPayload(value) })\n .catch((cause: unknown) => { if (!cancelled) setError(cause instanceof Error ? cause.message : String(cause)) })\n return () => { cancelled = true }\n }, [sessionId, timelineNonce])\n\n // Keep an open tab honest. Polling stops while the page is hidden, and a\n // returning page catches up immediately, so a background tab costs nothing and\n // a foreground one is never behind.\n useEffect(() => {\n if (sessionId === undefined || sessionId === '') return undefined\n const tick = (): void => {\n if (globalThis.document?.visibilityState === 'hidden') return\n setTimelineNonce((value) => value + 1)\n }\n const timer = globalThis.setInterval(tick, POLL_INTERVAL_MS)\n const onVisibility = (): void => { if (globalThis.document?.visibilityState !== 'hidden') tick() }\n globalThis.document?.addEventListener('visibilitychange', onVisibility)\n return () => {\n globalThis.clearInterval(timer)\n globalThis.document?.removeEventListener('visibilitychange', onVisibility)\n }\n }, [sessionId])\n\n useEffect(() => {\n if (openReport === undefined) return\n let cancelled = false\n setDetail(undefined)\n setDetailError(undefined)\n getJson<ReportDetail>(`${REPORT_URL}?id=${encodeURIComponent(openReport)}`)\n .then((value) => { if (!cancelled) setDetail(value) })\n .catch((cause: unknown) => { if (!cancelled) setDetailError(cause instanceof Error ? cause.message : String(cause)) })\n return () => { cancelled = true }\n }, [openReport, detailNonce])\n\n const rows = useMemo<Row[]>(() => (payload === undefined ? [] : buildRows(payload, basis)), [payload, basis])\n const counts = useMemo(() => statusCounts(payload?.reports ?? []), [payload])\n const inList = useMemo(() => (payload === undefined ? new Set<string>() : reportIds(payload)), [payload])\n // The lease state is not on the digest: liveness comes from the hop stream, and\n // the host derives it once so the browser never needs a second copy of the rule.\n const lanes = useMemo(() => (payload === undefined ? new Map<string, WorktreeLane>() : laneIndex(payload)), [payload])\n const shownBody = useMemo(() => displayBody(detail?.body ?? ''), [detail])\n // The text the renderer gets. A cut body can end mid-fence, and an unterminated\n // fence would turn the rest of the report into a code block — see\n // `closeOpenFence`. Only applied when the body was actually cut, so a whole\n // body always renders exactly as written.\n const bodyText = useMemo(() => {\n const text = shownBody.text.trim()\n return shownBody.truncated ? closeOpenFence(text) : text\n }, [shownBody])\n /** Strings for the prose renderer's own controls, from this plugin's dictionary. */\n const markdownLabels = useMemo(\n () => ({ code: { copyLabel: t('body.copy'), copiedLabel: t('body.copied') }, footnotes: t('body.footnotes') }),\n [t],\n )\n const filter = useMemo(() => ({ status, text }), [status, text])\n const visible = useMemo<Row[]>(() => {\n const kept = filterRows(rows, filter)\n if (openReport === undefined || detail === undefined) return kept\n // The panel is anchored to a report row, so the open report must have one —\n // see `withSynthesizedRow` for the two user paths that would otherwise leave\n // the panel with nowhere to render, and why the row is inserted in time\n // order instead of appended.\n return withSynthesizedRow(kept, detail.front, basis)\n }, [rows, filter, openReport, detail, basis])\n // Reports surviving the FILTER, as opposed to `visible`, which also carries a\n // synthesized row for the open report. Keeping the two apart is what lets the\n // panel say which of the two reasons applies: outside this tree, or hidden by\n // the current filter. Claiming \"another line\" for a filtered report would be\n // simply wrong.\n const filteredReportIds = useMemo(() => {\n const ids = new Set<string>()\n for (const row of filterRows(rows, filter)) if (row.kind === 'report') ids.add(row.front.report)\n return ids\n }, [rows, filter])\n const filtering = isFiltering(filter)\n // Counted from the unfiltered row set so a synthesized out-of-tree row cannot\n // inflate the summary.\n const visibleReportCount = useMemo(() => visibleReports(rows, filter), [rows, filter])\n // Which of the two \"nothing to show\" sentences applies. Decided in the pure\n // model rather than inline here, so `emptyState`'s tests pin it.\n const empty = useMemo(\n () => (payload === undefined ? 'none' : emptyState(payload, rows, filter)),\n [payload, rows, filter],\n )\n // Session titles, for the hover hint on a shortened id. Deliberately NOT the\n // row label: a title is the session's first prompt, so it is unbounded, while\n // a row has to stay one scannable line.\n const titles = useMemo(\n () => (payload === undefined ? new Map<string, string>() : sessionTitles(payload)),\n [payload],\n )\n const idHint = useCallback((id: string): string => {\n const title = titles.get(id)\n return title === undefined ? id : `${title} · ${id}`\n }, [titles])\n\n /**\n * The reports the topology plots.\n *\n * Taken from the same filtered row set the list renders, so the graph is a view\n * of the current filter rather than a second source of truth — narrowing the\n * toolbar narrows both.\n */\n const plotted = useMemo(\n () => filterRows(rows, filter).flatMap((row) => (row.kind === 'report' ? [row.front] : [])),\n [rows, filter],\n )\n /**\n * One session id the way a dense row shows it: shortened, with the full value\n * and the session title on hover. The hover is not decoration — it is where\n * the human name lives, because titles are unbounded and a row is not.\n */\n const idRef = (id: string): ReactElement => (\n <Tooltip label={idHint(id)} side=\"bottom\" delayMs={400}>\n <span>{shortId(id)}</span>\n </Tooltip>\n )\n\n const refresh = useCallback(() => {\n setTimelineNonce((value) => value + 1)\n setDetailNonce((value) => value + 1)\n }, [])\n\n /**\n * Open a report from the topology.\n *\n * The graph does not get its own detail surface: it opens the card in the list\n * and brings it into view, which keeps one reading surface for the body and the\n * transfer path — and inherits the card's guarantees for free (a filtered-out\n * report still gets a row to render into).\n */\n const openFromTopology = useCallback((report: string) => {\n scrollTo.current = report\n setOpenReport(report)\n }, [])\n\n // Scroll after the row exists, not before: the card may only be synthesized by\n // the render this state change is about to cause.\n useEffect(() => {\n const target = scrollTo.current\n if (target === undefined || openReport !== target) return\n scrollTo.current = undefined\n globalThis.document?.getElementById(rowDomId(target))?.scrollIntoView({ block: 'center', behavior: 'smooth' })\n }, [openReport])\n\n /**\n * Copy the ledger path, acknowledging through the control's own label.\n *\n * Uses the shell's `writeClipboard` rather than `navigator.clipboard`, so the\n * plugin inherits whatever fallback chain the rest of the GUI relies on.\n */\n const copyPath = useCallback((path: string) => {\n void writeClipboard(path).then((ok) => {\n if (!ok) return\n setCopiedPath(true)\n if (copyTimer.current !== undefined) globalThis.clearTimeout(copyTimer.current)\n copyTimer.current = globalThis.setTimeout(() => { setCopiedPath(false) }, COPIED_HOLD_MS)\n })\n }, [])\n\n const shell: Record<string, string | number> = {\n padding: '16px 20px',\n overflowY: 'auto',\n height: '100%',\n font: 'var(--dsw-font-xs-13, 13px/1.6 system-ui, sans-serif)',\n color: 'var(--dsw-alias-label-primary, inherit)',\n }\n const head: Record<string, string | number> = {\n display: 'flex',\n alignItems: 'baseline',\n gap: '12px',\n marginBottom: '12px',\n }\n const summary: Record<string, string | number> = {\n color: 'var(--dsw-alias-label-tertiary, #888)',\n fontSize: 'var(--dsw-font-xxs-12, 12)',\n flex: '1 1 auto',\n }\n /**\n * The toolbar's search seat.\n *\n * `Input` renders its own wrapper around the field, so the flex sizing lives\n * on this outer box rather than on the control itself.\n */\n const searchBox: Record<string, string | number> = {\n flex: '1 1 200px',\n minWidth: '140px',\n display: 'flex',\n }\n const tools: Record<string, string | number> = {\n display: 'flex',\n alignItems: 'center',\n flexWrap: 'wrap',\n gap: '6px',\n marginBottom: '10px',\n }\n /** Chips sit next to a label; the shell's tags carry no spacing of their own. */\n const tagRow: Record<string, string | number> = {\n display: 'inline-flex',\n gap: '4px',\n marginRight: '6px',\n verticalAlign: 'baseline',\n }\n /** Keeps the state dot on the row's text baseline. */\n const dotCell: Record<string, string | number> = {\n display: 'inline-flex',\n alignItems: 'center',\n }\n const time: Record<string, string | number> = {\n color: 'var(--dsw-alias-label-caption, #999)',\n fontSize: 'var(--dsw-font-xxxs-11, 11)',\n whiteSpace: 'nowrap',\n }\n /**\n * The same caption look, for content that must be allowed to WRAP.\n *\n * `time` sets `white-space: nowrap`, which is right for a timestamp and wrong\n * for anything whose length the ledger decides. A nowrap element has a\n * min-content width equal to its whole unbreakable line, so inside a `1fr`\n * grid track — or a flex item without `min-width: 0` — it grows the track past\n * its container. Recipient lists and hop notes are exactly that: unbounded\n * text, which is how the transfer path used to push the whole tab into a\n * horizontal scrollbar and cut the notes off screen.\n */\n const wrap: Record<string, string | number> = {\n ...time,\n whiteSpace: 'normal',\n minWidth: 0,\n overflowWrap: 'anywhere',\n }\n /** The muted caption colour, for the empty-state sentences. */\n const muted: Record<string, string | number> = {\n color: 'var(--dsw-alias-label-tertiary, #888)',\n }\n /** The frame the rendered body sits in — a block, not a scroll box. */\n const bodyFrame: Record<string, string | number> = {\n padding: '8px 10px',\n background: 'var(--dsw-alias-bg-base, rgba(0,0,0,0.02))',\n border: '1px solid var(--dsw-alias-border-l1, rgba(0,0,0,0.06))',\n borderRadius: '4px',\n }\n /** The transcript's footer line: ledger path plus its copy control. */\n const pathRow: Record<string, string | number> = {\n ...wrap,\n marginTop: '6px',\n display: 'flex',\n alignItems: 'baseline',\n gap: '6px',\n }\n /** The topology's frame: an overview that sits above the list, not instead of it. */\n const topoSection: Record<string, string | number> = {\n marginBottom: '12px',\n border: '1px solid var(--dsw-alias-border-l1, rgba(0,0,0,0.06))',\n borderRadius: '6px',\n background: 'var(--dsw-alias-bg-layer-1, rgba(0,0,0,0.02))',\n overflow: 'hidden',\n }\n const topoHead: Record<string, string | number> = {\n display: 'flex',\n alignItems: 'center',\n gap: '10px',\n padding: '6px 10px',\n borderBottom: '1px solid var(--dsw-alias-border-l1, rgba(0,0,0,0.06))',\n }\n\n if (error !== undefined) {\n return (\n <div style={shell}>\n <div style={{ display: 'flex', alignItems: 'center', gap: '6px' }}>\n <StateDot state=\"error\" size={8} />\n <span>{t('view.error', { code: error })}</span>\n </div>\n <div style={{ marginTop: '8px' }}>\n <Button variant=\"outline\" onClick={refresh}>{t('view.retry')}</Button>\n </div>\n </div>\n )\n }\n\n if (payload === undefined) {\n return <div style={shell}>{t('view.loading')}</div>\n }\n\n const statusLabel: Record<StatusFilter, ReportLedgerKey> = {\n all: 'filter.all',\n open: 'filter.open',\n acked: 'filter.acked',\n closed: 'filter.closed',\n }\n const statusCount: Record<StatusFilter, number> = {\n all: counts.all,\n open: counts.open,\n acked: counts.acked,\n closed: counts.closed,\n }\n const chips: StatusFilter[] = ['all', 'open', 'acked', 'closed']\n\n return (\n <div style={shell}>\n <div style={head}>\n <strong>{t('view.tab')}</strong>\n <span style={summary}>\n {filtering\n ? t('filter.showing', { visible: visibleReportCount, total: counts.all })\n : t('view.summary', { sessions: payload.sessions.length, reports: payload.reports.length })}\n </span>\n <Button variant=\"outline\" icon={<IconRefreshOutlineRegular size={16} />} onClick={refresh}>{t('view.refresh')}</Button>\n </div>\n\n <div style={tools} title={t('filter.hint')}>\n {chips.map((candidate) => (\n <Pill\n key={candidate}\n active={status === candidate}\n aria-pressed={status === candidate}\n onClick={() => { setStatus(candidate) }}\n >\n {`${t(statusLabel[candidate])} ${statusCount[candidate]}`}\n </Pill>\n ))}\n <div style={searchBox}>\n <Input\n type=\"search\"\n className={SEARCH_CLASS}\n value={text}\n placeholder={t('filter.search')}\n aria-label={t('filter.search')}\n onChange={(event) => { setText(event.target.value) }}\n />\n </div>\n {filtering ? (\n <Button variant=\"outline\" onClick={() => { setStatus('all'); setText('') }}>{t('filter.clear')}</Button>\n ) : null}\n <Tooltip label={t('basis.hint')} side=\"bottom\" delayMs={400}>\n <Button variant=\"outline\" onClick={() => { setBasis(basis === 'created' ? 'updated' : 'created') }}>\n {t(basis === 'created' ? 'basis.created' : 'basis.updated')}\n </Button>\n </Tooltip>\n </div>\n\n {empty === 'no-reports' ? <div style={muted}>{t('view.empty')}</div> : null}\n {empty === 'filtered-out' ? <div style={muted}>{t('filter.none')}</div> : null}\n\n {payload !== undefined && plotted.length > 0 ? (\n <section style={topoSection}>\n <div style={topoHead}>\n <strong>{t('topology.title')}</strong>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n icon={showTopology ? <IconChevronDownOutlineRegular size={14} /> : <IconChevronRightOutlineRegular size={14} />}\n onClick={() => { setShowTopology((value) => !value) }}\n >\n {showTopology ? t('topology.hide') : t('topology.show')}\n </Button>\n </div>\n {showTopology ? (\n <CardgraphView\n payload={payload}\n reports={plotted}\n t={t}\n idHint={idHint}\n openReport={openReport}\n onOpen={openFromTopology}\n />\n ) : null}\n </section>\n ) : null}\n\n {visible.map((row, index) => {\n const pad = 8 + row.depth * 18\n if (row.kind === 'session') {\n return (\n <div key={`s-${row.id}-${index}`} style={{ display: 'flex', gap: '8px', padding: `4px 0 4px ${pad}px`, alignItems: 'baseline' }}>\n {/* The shell's state dot, not a text bullet: `ongoing` is the\n animated one, so a resident session reads as alive at a glance. */}\n <Tooltip label={idHint(row.id)} side=\"bottom\" delayMs={400}>\n <span style={dotCell}><StateDot state={row.live ? 'ongoing' : 'idle'} size={8} /></span>\n </Tooltip>\n <span style={{ flex: '1 1 auto', minWidth: 0 }}>\n {row.delegated || row.live ? (\n <span style={tagRow}>\n {row.delegated ? <Tag tone=\"info\">{t('view.delegated')}</Tag> : null}\n {row.live ? <Tag tone=\"success\">{t('view.live')}</Tag> : null}\n </span>\n ) : null}\n {row.title ?? shortId(row.id)}\n </span>\n {/* The session's own short id, so a digest row's `from=`/`to=` can be\n read back to the tree it belongs to. Titles stay the prominent\n label — this is the cross-reference, not the identity. */}\n {row.title === undefined ? null : (\n <Tooltip label={row.id} side=\"right\" delayMs={400}>\n <span style={time}>{shortId(row.id)}</span>\n </Tooltip>\n )}\n <span style={time}>{stamp(row.at)}</span>\n </div>\n )\n }\n const front = row.front\n const expanded = openReport === front.report\n const panelId = `report-ledger-panel-${front.report}`\n // The label comes off the binding, so a report opened by id and rendered\n // before it is in the payload still names its checkout; only the lease\n // state needs the lane, and a missing lane degrades to `none`.\n const wtLabel = worktreeLabel(front.worktree)\n const wtLane = lanes.get(front.report)\n const toggleLabel = fill(expanded ? t('row.collapse') : t('row.expand'), { report: front.report })\n return (\n <div key={`r-${front.report}`} id={rowDomId(front.report)} style={{ paddingLeft: `${pad}px` }}>\n {/* The row is a plain container, not a `role=\"button\"`.\n It used to be one, and it contained a real <button> (the task\n chip) — nested interactive content. Worse, a role=\"button\" takes\n its accessible name from its whole subtree, so a screen reader\n read out a line of session uuids and then the task label twice,\n once inside the row's own name.\n The disclosure now lives on the leading control: the shell's own\n Button, whose name says what it does and whose `aria-controls`\n points at the panel it opens. Clicking anywhere on the row stays\n as the pointer convenience, which is why the container keeps a\n click handler with no role: the keyboard path is the button, and\n the pointer path is the row. */}\n <div\n onClick={() => { setOpenReport(expanded ? undefined : front.report) }}\n style={{\n display: 'flex',\n gap: '8px',\n alignItems: 'baseline',\n padding: '5px 8px',\n margin: '2px 0',\n borderRadius: '6px',\n cursor: 'pointer',\n background: 'var(--dsw-alias-bg-layer-1, rgba(0,0,0,0.02))',\n border: '1px solid var(--dsw-alias-border-l1, rgba(0,0,0,0.06))',\n }}\n >\n <Button\n variant=\"ghost\"\n size=\"sm\"\n icon={expanded ? <IconChevronDownOutlineRegular size={14} /> : <IconChevronRightOutlineRegular size={14} />}\n aria-expanded={expanded}\n aria-controls={panelId}\n aria-label={toggleLabel}\n title={toggleLabel}\n onClick={(event) => {\n // The row's own click handler is the same action; without this\n // the one click would toggle twice and appear to do nothing.\n event.stopPropagation()\n setOpenReport(expanded ? undefined : front.report)\n }}\n />\n <span style={{ flex: '1 1 auto', minWidth: 0 }}>\n <span style={tagRow}><Tag tone={STATUS_TONE[front.status]}>{t(STATUS_LABEL[front.status])}</Tag></span>\n <strong>{front.report}</strong> {front.subject}\n {/* The task chip is a filter shortcut, not decoration: clicking it\n reuses the search box, so grouping by collaboration needs no\n state of its own. It stops the click from also toggling the\n detail panel, and it CLEARS the status filter — its own label\n promises \"show only this task\", which stops being true the\n moment a status chip is also narrowing the list. */}\n {front.task === undefined ? null : (\n <Pill\n title={fill(t('row.filterByTask'), { task: front.task })}\n onClick={(event) => {\n event.stopPropagation()\n setStatus('all')\n setText(front.task as string)\n }}\n >\n {front.task}\n </Pill>\n )}\n {/* The checkout chip follows the task chip exactly, for the same\n reason: it reuses the search box instead of inventing a second\n filter, and it clears the status filter because its own label\n promises \"show only this checkout\". Its tone is the lease\n state, which is the one thing the digest cannot carry — a\n checkout nobody holds and one being worked in look identical\n from the binding alone. */}\n {wtLabel === undefined ? null : (\n <Tag tone={LEASE_TONE[wtLane?.state ?? 'none']}>\n <span\n title={`${fill(t('row.filterByWorktree'), { worktree: wtLabel })} — ${t(LEASE_HINT[wtLane?.state ?? 'none'])}`}\n style={{ cursor: 'pointer' }}\n onClick={(event) => {\n event.stopPropagation()\n setStatus('all')\n setText(wtLabel)\n }}\n >\n {wtLabel}\n </span>\n </Tag>\n )}\n {/* One line per report is the point of the digest, so the meta\n line is clipped rather than wrapped: a single over-connected\n report must not turn every row into a paragraph. `min-width: 0`\n on the parent is what lets the ellipsis happen at all — without\n it the flex item cannot shrink below its content, and the row\n pushes the whole tab sideways instead. */}\n <div style={{ ...time, marginTop: '2px', overflow: 'hidden', textOverflow: 'ellipsis' }}>\n {`from=`}{idRef(front.from)}\n {front.to.length > 0 ? (\n <>\n {' → '}\n {front.to.map((id, index) => (\n <Fragment key={id}>{index > 0 ? ', ' : ''}{idRef(id)}</Fragment>\n ))}\n </>\n ) : null}\n {front.cc.length > 0 ? (\n <>\n {' cc '}\n {front.cc.map((id, index) => (\n <Fragment key={id}>{index > 0 ? ', ' : ''}{idRef(id)}</Fragment>\n ))}\n </>\n ) : null}\n {` · ${fill(t('row.hops'), { count: front.hops })}`}\n {front.authors.length > 1 ? ` · ${fill(t('row.authors'), { count: front.authors.length })}` : ''}\n {/* The last hop's action is ledger vocabulary, so it is named\n with the same word the transfer path uses below. */}\n {front.last === undefined ? '' : ` · ${t('row.last')} ${t(HOP_LABEL[front.last.action])}`}\n </div>\n </span>\n <span style={time}>{stamp(front.updated)}</span>\n </div>\n\n {expanded ? (\n <div id={panelId} style={{\n margin: '4px 0 10px 24px',\n padding: '10px 12px',\n borderRadius: '6px',\n background: 'var(--dsw-alias-bg-layer-2, rgba(0,0,0,0.03))',\n border: '1px solid var(--dsw-alias-border-l1, rgba(0,0,0,0.06))',\n }}>\n {detailError !== undefined ? <div style={{ color: 'var(--dsw-alias-state-error-label, #b00)' }}>{t('view.error', { code: detailError })}</div> : null}\n {detail === undefined && detailError === undefined ? <div style={time}>{t('view.detail.loading')}</div> : null}\n {detail === undefined ? null : (\n <>\n <div style={{ marginBottom: '6px' }}><strong>{t('view.detail.route')}</strong></div>\n {detail.hops.length === 0 ? <div style={time}>{t('view.detail.routeEmpty')}</div> : (\n <div style={{ display: 'grid', gridTemplateColumns: 'auto auto auto 1fr', gap: '2px 10px', marginBottom: '8px' }}>\n {detail.hops.map((hop, hopIndex) => (\n <Fragment key={`h-${hopIndex}`}>\n <span style={time}>{stamp(hop.at)}</span>\n <span><strong>{t(HOP_LABEL[hop.action])}</strong></span>\n <span style={wrap}><Tooltip label={idHint(hop.actor)} side=\"bottom\" delayMs={400}><span>{hop.actor}</span></Tooltip></span>\n <span style={wrap}>{hop.to.length === 0 ? (hop.note ?? '') : `→ ${hop.to.join(', ')}${hop.note === undefined ? '' : ` (${hop.note})`}`}</span>\n </Fragment>\n ))}\n </div>\n )}\n {/* \"Still owed\" is a state, so it gets the shell's state dot\n instead of a colour mixed by hand. */}\n {detail.pending.length === 0 ? null : (\n <div style={{ ...wrap, display: 'flex', alignItems: 'baseline', gap: '6px' }}>\n <StateDot state=\"warning\" size={8} />\n <span>{fill(t('view.detail.pending'), { targets: detail.pending.join(', ') })}</span>\n </div>\n )}\n\n {(() => {\n const parent = detail.front.parent\n const children = detail.front.children\n if (parent === undefined && children.length === 0) return null\n const link = (target: string): ReactElement => (\n <span key={target} style={{ marginRight: '6px' }}>\n <Tooltip label={fill(t('detail.openLinked'), { report: target })} side=\"bottom\" delayMs={400}>\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => { setOpenReport(target) }}\n >\n {target}\n </Button>\n </Tooltip>\n </span>\n )\n return (\n <div style={{ marginBottom: '8px' }}>\n <strong>{t('detail.thread')}</strong>\n {parent === undefined ? null : (\n <div style={{ marginTop: '2px' }}>\n <span style={time}>{`${t('detail.parent')}: `}</span>{link(parent)}\n </div>\n )}\n {children.length === 0 ? null : (\n <div style={{ marginTop: '2px' }}>\n <span style={time}>{`${t('detail.children')}: `}</span>{children.map(link)}\n </div>\n )}\n </div>\n )\n })()}\n\n {/* Kept OUT of the thread block above: that block returns early\n when a report has no thread links, which is the common case\n for exactly the reports this notice is about. */}\n {inList.has(detail.front.report) ? null : (\n <div style={{ ...wrap, marginBottom: '8px' }}>\n {fill(t('detail.outside'), { report: detail.front.report })}\n {' '}\n <Button variant=\"outline\" onClick={() => { setOpenReport(undefined) }}>\n {t('detail.backToList')}\n </Button>\n </div>\n )}\n {inList.has(detail.front.report) && !filteredReportIds.has(detail.front.report) ? (\n <div style={{ ...wrap, marginBottom: '8px' }}>\n {fill(t('detail.filteredOut'), { report: detail.front.report })}\n </div>\n ) : null}\n\n <div style={{ margin: '8px 0 4px' }}><strong>{t('view.detail.body')}</strong></div>\n {/* Rendered with the shell's own prose renderer — the same one\n the Chat and Trajectory views use — so a report body reads\n like everything else in the GUI instead of like a raw file.\n\n No inner scroll box on purpose. The text is already bounded\n by the same limit `report_read` gives the model, so the panel\n grows by at most a few dozen lines; a nested scroller inside\n the timeline's scroller would only steal the wheel. */}\n <div style={bodyFrame}>\n <MarkdownText text={bodyText} labels={markdownLabels} />\n </div>\n {shownBody.truncated ? (\n <div style={{ ...wrap, marginTop: '4px' }}>\n {fill(t('detail.bodyTruncated'), { chars: shownBody.text.trim().length })}\n </div>\n ) : null}\n {/* The ledger path is the longest string on the panel and the\n only one that carries no spaces to break at, so it wraps\n mid-segment on purpose — and it now has a copy control,\n because \"see the file\" is only useful if you can get there.\n The copy goes through the shell's own `writeClipboard`. */}\n <div style={pathRow}>\n <StateDot state=\"idle\" size={6} />\n <span style={{ wordBreak: 'break-all' }}>{t('view.detail.path')}: {detail.path}</span>\n <Tooltip label={copiedPath ? t('body.copied') : t('view.copyPath')} side=\"bottom\" delayMs={300}>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n icon={copiedPath ? <IconCheckOutlineRegular size={16} /> : <IconCopyOutlineRegular size={16} />}\n aria-label={copiedPath ? t('body.copied') : t('view.copyPath')}\n onClick={() => { copyPath(detail.path) }}\n />\n </Tooltip>\n </div>\n </>\n )}\n </div>\n ) : null}\n </div>\n )\n })}\n </div>\n )\n}\n","/**\n * Browser half of dsh-report-ledger — runs inside the dsh web GUI.\n *\n * The shell serves the built `lib/client.js` from /plugins/dsh-report-ledger/client.js\n * and evaluates it as a closure-factory artifact, so this module's `require` is\n * answered by the loader's frozen module table (react, cordis, the slot\n * services) and every other dependency is inlined by the build.\n *\n * It contributes exactly one thing: a `conversation.view` entry, which the\n * conversation shell projects as a tab beside the shipped Chat and Trajectory\n * views. A fresh id is used rather than an existing one, so nothing shipped is\n * replaced.\n *\n * @module dsh-report-ledger/client\n */\n\nimport { createElement } from 'react'\nimport { ReportsView, type Translate } from './ReportsView.tsx'\nimport { en, zh } from './locales.ts'\nimport { installStyles } from './styles.ts'\n\n/** Locale namespace this plugin owns. */\nconst NS = 'report-ledger'\n\n/** Tab key. Distinct from the shipped `chat` / `trajectory` cells. */\nconst VIEW_ID = 'report-ledger'\n\n/** Position after the shipped views (`chat` 0, `trajectory` 10). */\nconst VIEW_ORDER = 20\n\n/** Minimal structural face of the client slot service. */\ninterface SlotsService {\n inject(key: string, callback: () => () => void): () => void\n register(options: Record<string, unknown>, component: (props: never) => unknown): () => void\n}\n\n/** Minimal structural face of the client locale service. */\ninterface LocaleService {\n register(ns: string, dicts: { zh: unknown; en: unknown }): () => void\n bind(ns: string): Translate\n}\n\n/**\n * Minimal structural face of the client plugin context.\n *\n * Deliberately local rather than imported: the client runtime's published types\n * live in packages seeded into the shell bundle, which are not part of a\n * third-party plugin's resolvable dependency set (and one of them is not even\n * installed here). The shape asserted is exactly what the registration below\n * uses, and the host-side declarations remain the source of truth for the wire.\n */\ninterface ClientContext {\n slots: SlotsService\n locale: LocaleService\n effect(callback: () => (() => void) | void, label?: string): () => void\n get(name: string): unknown\n}\n\n/** The slot and locale services must be mounted before this half can contribute. */\nexport const inject = ['slots', 'locale']\n\n/**\n * Apply the browser half.\n * @param ctx - the client plugin context (`slots`, `locale` injected).\n */\nexport function apply(ctx: ClientContext): void {\n ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'report-ledger: dictionaries')\n\n // Layout helpers the shell cannot know about — see client/styles.ts. Registered\n // as an effect so cordis_stop/undefine takes the sheet down with the plugin.\n ctx.effect(() => installStyles(), 'report-ledger: adapter styles')\n\n const t = ctx.locale.bind(NS)\n\n ctx.slots.inject('conversation.view', () => ctx.slots.register(\n {\n name: 'conversation.view',\n id: VIEW_ID,\n order: VIEW_ORDER,\n // A thunk is re-read on every projection, so the tab label follows the\n // active locale without re-registering.\n label: () => t('view.tab'),\n locale: NS,\n },\n (props: Record<string, unknown>) => createElement(ReportsView, { ...props, t } as never) as never,\n ))\n}\n"],"mappings":"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"}