pi-mega-compact 0.16.0 → 0.16.2
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/dist/extensions/dashboard-server/api-contracts/endpoints/registry.js +7 -0
- package/dist/extensions/dashboard-server/api-contracts/model-thresholds.js +1 -0
- package/dist/extensions/dashboard-server/routes-model-thresholds.js +167 -0
- package/dist/extensions/dashboard-server/routes.js +3 -2
- package/dist/extensions/dashboard-server/server.js +6 -2
- package/dist/extensions/mega-events/context-handler.js +37 -11
- package/dist/src/store/sqlite/model-snapshots.js +21 -0
- package/dist/src/store/sqlite/model-thresholds.js +93 -0
- package/dist/src/store/sqlite/schema/core.js +10 -0
- package/dist/src/store/sqlite.js +1 -0
- package/dist/src/vector-search.js +1 -1
- package/extensions/dashboard-client/dist/assets/{AreaChart-CrmTcE7w.js → AreaChart-Y_X0Bbjd.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{AreaChart-CrmTcE7w.js.map → AreaChart-Y_X0Bbjd.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{BarChart-Cf8DTaXS.js → BarChart-B9XDb-Ob.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{BarChart-Cf8DTaXS.js.map → BarChart-B9XDb-Ob.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{CacheTab-Ce0MES6p.js → CacheTab-BUGVb1G1.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{CacheTab-Ce0MES6p.js.map → CacheTab-BUGVb1G1.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{EventsTab-DbaUuA58.js → EventsTab-dOqLYTsy.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{EventsTab-DbaUuA58.js.map → EventsTab-dOqLYTsy.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{HealthTab-BmEXkcfL.js → HealthTab-c2vezKg1.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{HealthTab-BmEXkcfL.js.map → HealthTab-c2vezKg1.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{MaintenanceTab-n2JQrMy7.js → MaintenanceTab-bQYHIcdx.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MaintenanceTab-n2JQrMy7.js.map → MaintenanceTab-bQYHIcdx.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/MemoryMapTab-2L8pH6y2.js +2 -0
- package/extensions/dashboard-client/dist/assets/{MemoryMapTab-Dvu7vkOm.js.map → MemoryMapTab-2L8pH6y2.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{MetricsTab-Q0g32AmP.js → MetricsTab-CzUx1jpo.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MetricsTab-Q0g32AmP.js.map → MetricsTab-CzUx1jpo.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{OverviewTab-iKiEcOLL.js → OverviewTab-eCNoaclv.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{OverviewTab-iKiEcOLL.js.map → OverviewTab-eCNoaclv.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{ReposTab-DWpnv77y.js → ReposTab-tb0IW3NF.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{ReposTab-DWpnv77y.js.map → ReposTab-tb0IW3NF.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{SessionsTab-G0YxR1vG.js → SessionsTab-C-IoLw5h.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{SessionsTab-G0YxR1vG.js.map → SessionsTab-C-IoLw5h.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/SetupTab-BvmFi8gf.js +2 -0
- package/extensions/dashboard-client/dist/assets/SetupTab-BvmFi8gf.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/{TimeSavedCard-1_LCFiJp.js → TimeSavedCard-DBH-cOlA.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{TimeSavedCard-1_LCFiJp.js.map → TimeSavedCard-DBH-cOlA.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{TurnsTab-pqOkjE4L.js → TurnsTab-DBMgmvnX.js} +3 -3
- package/extensions/dashboard-client/dist/assets/{TurnsTab-pqOkjE4L.js.map → TurnsTab-DBMgmvnX.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/WikiTab-1LcQrXk3.js +2 -0
- package/extensions/dashboard-client/dist/assets/{WikiTab-99sKE4nB.js.map → WikiTab-1LcQrXk3.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/button-DtEsZTUE.js +2 -0
- package/extensions/dashboard-client/dist/assets/{button-YcV2VQEP.js.map → button-DtEsZTUE.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{card-Bwxzo50p.js → card-DcfTmw-4.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{card-Bwxzo50p.js.map → card-DcfTmw-4.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{generateCategoricalChart-C6hlfMYG.js → generateCategoricalChart-BsIxUDyw.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{generateCategoricalChart-C6hlfMYG.js.map → generateCategoricalChart-BsIxUDyw.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{index-DRa2d4TF.js → index-B93cewiX.js} +14 -14
- package/extensions/dashboard-client/dist/assets/index-B93cewiX.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/index-C_tCbOI0.css +1 -0
- package/extensions/dashboard-client/dist/assets/{switch-rU-lor0v.js → switch-Bowd7jCD.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{switch-rU-lor0v.js.map → switch-Bowd7jCD.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{toggle-h-9B4w9X.js → toggle-Bo7kJZ4u.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{toggle-h-9B4w9X.js.map → toggle-Bo7kJZ4u.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{useSSE-BxJORWx8.js → useSSE-zNhb0c3u.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{useSSE-BxJORWx8.js.map → useSSE-zNhb0c3u.js.map} +1 -1
- package/extensions/dashboard-client/dist/index.html +2 -2
- package/extensions/dashboard-client/src/api/client.ts +38 -13
- package/extensions/dashboard-client/src/tabs/SetupTab/ThresholdsPanel.tsx +334 -0
- package/extensions/dashboard-client/src/tabs/SetupTab.tsx +9 -1
- package/extensions/dashboard-server/api-contracts/endpoints/registry.ts +50 -17
- package/extensions/dashboard-server/api-contracts/index.ts +28 -3
- package/extensions/dashboard-server/api-contracts/model-thresholds.ts +76 -0
- package/extensions/dashboard-server/routes-model-thresholds.ts +222 -0
- package/extensions/dashboard-server/routes.ts +11 -2
- package/extensions/dashboard-server/server.ts +5 -1
- package/extensions/mega-events/context-handler.ts +106 -69
- package/package.json +1 -1
- package/src/store/sqlite/model-snapshots.ts +99 -62
- package/src/store/sqlite/model-thresholds.ts +168 -0
- package/src/store/sqlite/schema/core.ts +10 -0
- package/src/store/sqlite.ts +1 -0
- package/src/vector-search.ts +1 -1
- package/extensions/dashboard-client/dist/assets/MemoryMapTab-Dvu7vkOm.js +0 -2
- package/extensions/dashboard-client/dist/assets/SetupTab-C-y6H-F-.js +0 -2
- package/extensions/dashboard-client/dist/assets/SetupTab-C-y6H-F-.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/WikiTab-99sKE4nB.js +0 -2
- package/extensions/dashboard-client/dist/assets/button-YcV2VQEP.js +0 -2
- package/extensions/dashboard-client/dist/assets/index-DRa2d4TF.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/index-za_gmEmQ.css +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TurnsTab-pqOkjE4L.js","sources":["../../src/components/HydeDetailPanel.tsx","../../src/tabs/TurnsTab.tsx"],"sourcesContent":["/**\n * dashboard-client/src/components/HydeDetailPanel.tsx — per-turn HyDE + recall\n * quality detail (H3.1).\n *\n * Fetches /api/rag-metrics once and indexes telemetry rows by the composite\n * `conversationId:turnIndex` key (the dashboard TurnRow has no DB id, so the\n * pair is the stable identifier). Renders one of three HyDE states:\n * - HyDE ran: hypothetical doc (collapsible), raw/hyde/fused hit counts,\n * lift multiplier, generation latency.\n * - HyDE skipped: reason (disabled / no-llm / generation-failed).\n * - No telemetry: nothing (the parent only renders us when flagged).\n * Plus a CRAG-style mini score line when recall metrics exist.\n *\n * PREVENT-PI-004: relative-path fetch to the same-origin dashboard server.\n */\n\nimport type React from \"react\";\nimport { useMemo, useState } from \"react\";\nimport { useApi } from \"../hooks/useApi\";\nimport { fetchRagMetrics } from \"../api/client\";\nimport type { RagMetricsResponse } from \"@contracts\";\nimport { Card } from \"./ui/card\";\nimport { Badge } from \"./ui/badge\";\n\n/** Per-turn telemetry row shape derived from the rag-metrics contract. */\ntype TelemetryRow = RagMetricsResponse[\"recent\"][number];\n\nexport interface HydeDetailPanelProps {\n\tconversationId: string;\n\tturnIndex: number;\n}\n\n/** Composite key matching a telemetry row to a rendered turn. */\nfunction rowKey(conversationId: string, turnIndex: number): string {\n\treturn `${conversationId}:${turnIndex}`;\n}\n\n/** First N lines of the hypothetical doc (or full when expanded). */\nfunction truncateDoc(doc: string, maxLines: number): string {\n\tconst lines = doc.split(\"\\n\");\n\treturn lines.length > maxLines ? lines.slice(0, maxLines).join(\"\\n\") : doc;\n}\n\n/** CRAG-style mini score line when recall metrics were captured. */\nfunction ScoreLine({ row }: { row: TelemetryRow }): React.ReactElement {\n\tconst hasScore = row.recallScore > 0 || row.recallPass === 1;\n\tif (!hasScore) {\n\t\treturn (\n\t\t\t<div className=\"mt-2 text-xs text-muted-foreground\">\n\t\t\t\tNo recall-quality metrics for this turn.\n\t\t\t</div>\n\t\t);\n\t}\n\treturn (\n\t\t<div className=\"mt-2 rounded-md border border-border/60 bg-bg-elevated/40 p-2\">\n\t\t\t<div className=\"flex items-center gap-2 text-xs\">\n\t\t\t\t<span className=\"font-medium text-muted-foreground\">Recall quality</span>\n\t\t\t\t<Badge variant={row.recallPass === 1 ? \"success\" : \"warning\"}>\n\t\t\t\t\t{row.recallPass === 1 ? \"pass\" : \"fail\"}\n\t\t\t\t</Badge>\n\t\t\t\t<span>\n\t\t\t\t\tscore <strong className=\"text-foreground\">{row.recallScore.toFixed(2)}</strong>\n\t\t\t\t</span>\n\t\t\t</div>\n\t\t\t<div className=\"mt-1 flex flex-wrap gap-x-4 gap-y-0.5 text-xs text-muted-foreground\">\n\t\t\t\t<span>relevance {row.recallRelevance.toFixed(2)}</span>\n\t\t\t\t<span>coverage {row.recallCoverage.toFixed(2)}</span>\n\t\t\t\t<span>diversity {row.recallDiversity.toFixed(2)}</span>\n\t\t\t\t<span>specificity {row.recallSpecificity.toFixed(2)}</span>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nexport const HydeDetailPanel: React.FC<HydeDetailPanelProps> = ({\n\tconversationId,\n\tturnIndex,\n}) => {\n\tconst { data: metrics } = useApi<RagMetricsResponse>(\n\t\tuseMemo(() => () => fetchRagMetrics(), []),\n\t\t{ pollInterval: 30_000 },\n\t);\n\tconst [showFullDoc, setShowFullDoc] = useState(false);\n\n\t// Rebuild the index whenever metrics arrive; rare enough that a full\n\t// rebuild on each render is cheaper than memoizing with a mutable dep.\n\tconst row = useMemo(\n\t\t() =>\n\t\t\tmetrics?.recent.find(\n\t\t\t\t(t) => rowKey(t.conversationId, t.turnIndex) === rowKey(conversationId, turnIndex),\n\t\t\t) ?? null,\n\t\t[metrics, conversationId, turnIndex],\n\t);\n\n\tif (!metrics) {\n\t\treturn <div className=\"text-xs text-muted-foreground\">Loading HyDE stats…</div>;\n\t}\n\tif (!row) {\n\t\treturn (\n\t\t\t<div className=\"text-xs text-muted-foreground\">\n\t\t\t\tNo HyDE/recall telemetry recorded for this turn.\n\t\t\t</div>\n\t\t);\n\t}\n\n\tconst doc = row.hydeDoc ?? \"\";\n\tconst docLines = doc.split(\"\\n\").length;\n\tconst showDoc = docLines > 3;\n\tconst visibleDoc = showFullDoc || !showDoc ? doc : truncateDoc(doc, 3);\n\n\treturn (\n\t\t<Card className=\"border border-border/60 bg-bg-elevated/20 p-3\">\n\t\t\t{row.hydeRan === 1 ? (\n\t\t\t\t<>\n\t\t\t\t\t<div className=\"mb-2 flex flex-wrap items-center gap-2 text-xs\">\n\t\t\t\t\t\t<Badge variant=\"accent\">HyDE ran</Badge>\n\t\t\t\t\t\t<span className=\"text-muted-foreground\">\n\t\t\t\t\t\t\tgen <strong className=\"text-foreground\">{row.hydeGenerationMs}ms</strong>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span className=\"text-muted-foreground\">\n\t\t\t\t\t\t\traw <strong className=\"text-foreground\">{row.hydeRawCount}</strong>\n\t\t\t\t\t\t\t<Arrow />\n\t\t\t\t\t\t\thyde <strong className=\"text-foreground\">{row.hydeHydeCount}</strong>\n\t\t\t\t\t\t\t<Arrow />\n\t\t\t\t\t\t\tfused <strong className=\"text-foreground\">{row.hydeFusedCount}</strong>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span className=\"text-muted-foreground\">\n\t\t\t\t\t\t\tlift{\" \"}\n\t\t\t\t\t\t\t<strong className=\"text-neon\">{row.hydeLift.toFixed(2)}×</strong>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t{doc && (\n\t\t\t\t\t\t<pre className=\"max-h-40 overflow-auto whitespace-pre-wrap rounded-md border border-border/50 bg-bg-elevated/40 p-2 font-mono text-xs text-muted-foreground\">\n\t\t\t\t\t\t\t{visibleDoc}\n\t\t\t\t\t\t</pre>\n\t\t\t\t\t)}\n\t\t\t\t\t{showDoc && (\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\tclassName=\"mt-1 text-xs text-accent underline\"\n\t\t\t\t\t\t\tonClick={() => setShowFullDoc((v) => !v)}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{showFullDoc ? \"Show less\" : \"Show full hypothetical doc\"}\n\t\t\t\t\t\t</button>\n\t\t\t\t\t)}\n\t\t\t\t</>\n\t\t\t) : (\n\t\t\t\t<div className=\"flex items-center gap-2 text-xs\">\n\t\t\t\t\t<Badge variant=\"warning\">HyDE skipped</Badge>\n\t\t\t\t\t<span className=\"text-muted-foreground\">{skipReason(row)}</span>\n\t\t\t\t</div>\n\t\t\t)}\n\t\t\t<ScoreLine row={row} />\n\t\t</Card>\n\t);\n};\n\nfunction Arrow(): React.ReactElement {\n\treturn <span className=\"text-muted-foreground/60\"> → </span>;\n}\n\nfunction skipReason(row: TelemetryRow): string {\n\tswitch (row.hydeReason) {\n\t\tcase \"no-llm\":\n\t\t\treturn \"No LLM embedder available (requires Ollama/HTTP embedder).\";\n\t\tcase \"generation-failed\":\n\t\t\treturn \"Hypothetical doc generation failed this turn.\";\n\t\tcase \"disabled\":\n\t\t\treturn \"HyDE is disabled (MEGACOMPACT_HYDE_DISABLED).\";\n\t\tdefault:\n\t\t\treturn \"HyDE was not invoked for this turn.\";\n\t}\n}\n","/**\n * dashboard-client/src/tabs/TurnsTab.tsx — Turn-by-turn memory tracking + recall (S52).\n *\n * Fetches /api/turns (conversation list), expands the active conversation into\n * per-turn detail (/api/turns/conversation/:id), and renders:\n * - Conversation summary list (turn count, recall total, epochs, last turn).\n * - Per-turn rows: turn index, role, ctx tokens/percent + pressure band,\n * epoch id, and the injected-checkpoint recall set (the memory recalled\n * at each turn, with score + source).\n * - Pending rewind intents (S52A) + Fork action (S50C primitive).\n *\n * Read-mostly via /api/turns; fork/intent/prune via POST. No absolute URLs\n * (PREVENT-PI-004: loopback-only, same-origin static bundle).\n */\n\nimport type React from \"react\";\nimport { useCallback, useState } from \"react\";\nimport { useApi } from \"../hooks/useApi\";\nimport {\n\tfetchTurns,\n\tfetchConversationTurns,\n\tfetchTurnIntents,\n\tpostFork,\n\tpostTurnIntent,\n} from \"../api/client\";\nimport type {\n\tTurnsResponse,\n\tConversationTurnsResponse,\n\tRewindIntentsResponse,\n\tTurnRow,\n\tConversationSummary,\n} from \"@contracts\";\nimport { Card, CardContent, CardHeader, CardTitle } from \"../components/ui/card\";\nimport { Badge } from \"../components/ui/badge\";\nimport { Button } from \"../components/ui/button\";\nimport { HydeDetailPanel } from \"../components/HydeDetailPanel\";\n\nfunction fmtTs(ms: number): string {\n\tif (!ms) return \"—\";\n\treturn new Date(ms).toLocaleString();\n}\n\nfunction bandVariant(band: TurnRow[\"pressureBand\"]): \"success\" | \"warning\" | \"danger\" | \"default\" {\n\tif (band === \"green\") return \"success\";\n\tif (band === \"yellow\") return \"warning\";\n\tif (band === \"red\") return \"danger\";\n\treturn \"default\";\n}\n\nfunction sourceLabel(s: TurnRow[\"recall\"][number][\"source\"]): string {\n\tif (s === \"checkpoint\") return \"ckpt\";\n\tif (s === \"cluster_summary\") return \"raptor\";\n\treturn \"mem\";\n}\n\nexport default function TurnsTab(): React.ReactElement {\n\tconst {\n\t\tdata: turns,\n\t\tloading,\n\t\terror,\n\t} = useApi<TurnsResponse>(\n\t\tuseCallback(() => fetchTurns(), []),\n\t\t{\n\t\t\tpollInterval: 10_000,\n\t\t},\n\t);\n\tconst { data: intents } = useApi<RewindIntentsResponse>(\n\t\tuseCallback(() => fetchTurnIntents(), []),\n\t\t{ pollInterval: 5_000 },\n\t);\n\tconst [expanded, setExpanded] = useState<string | null>(null);\n\tconst [detail, setDetail] = useState<ConversationTurnsResponse | null>(null);\n\tconst [detailLoading, setDetailLoading] = useState(false);\n\tconst [busy, setBusy] = useState<string | null>(null);\n\tconst [notice, setNotice] = useState<string | null>(null);\n\n\tconst expand = useCallback(\n\t\tasync (conversationId: string) => {\n\t\t\tif (expanded === conversationId) {\n\t\t\t\tsetExpanded(null);\n\t\t\t\tsetDetail(null);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tsetExpanded(conversationId);\n\t\t\tsetDetailLoading(true);\n\t\t\tsetDetail(null);\n\t\t\ttry {\n\t\t\t\tconst d = await fetchConversationTurns(conversationId);\n\t\t\t\tsetDetail(d);\n\t\t\t} finally {\n\t\t\t\tsetDetailLoading(false);\n\t\t\t}\n\t\t},\n\t\t[expanded],\n\t);\n\n\tconst onFork = useCallback(\n\t\tasync (conversationId: string, turnIndex: number) => {\n\t\t\tsetBusy(`fork:${conversationId}:${turnIndex}`);\n\t\t\tsetNotice(null);\n\t\t\ttry {\n\t\t\t\tconst out = await postFork(conversationId, turnIndex);\n\t\t\t\tsetNotice(\n\t\t\t\t\t`Forked → ${out.childConversationId} (${out.recalledCount} recalled)`,\n\t\t\t\t);\n\t\t\t} catch (e) {\n\t\t\t\tsetNotice(`Fork failed: ${(e as Error).message}`);\n\t\t\t} finally {\n\t\t\t\tsetBusy(null);\n\t\t\t}\n\t\t},\n\t\t[],\n\t);\n\n\tconst onRewind = useCallback(\n\t\tasync (conversationId: string, turnIndex: number) => {\n\t\t\tsetBusy(`rewind:${conversationId}:${turnIndex}`);\n\t\t\tsetNotice(null);\n\t\t\ttry {\n\t\t\t\tawait postTurnIntent(conversationId, turnIndex);\n\t\t\t\tsetNotice(`Rewind intent queued for turn ${turnIndex}`);\n\t\t\t} catch (e) {\n\t\t\t\tsetNotice(`Rewind failed: ${(e as Error).message}`);\n\t\t\t} finally {\n\t\t\t\tsetBusy(null);\n\t\t\t}\n\t\t},\n\t\t[],\n\t);\n\n\tif (error && !turns) {\n\t\treturn <div className=\"tab-stub\">Error loading turns: {error.message}</div>;\n\t}\n\tif (loading && !turns) {\n\t\treturn <div className=\"tab-stub\">Loading turns…</div>;\n\t}\n\tif (!turns || turns.conversations.length === 0) {\n\t\treturn (\n\t\t\t<div className=\"tab-stub\">\n\t\t\t\t<h3>Turn-by-turn memory</h3>\n\t\t\t\t<p>\n\t\t\t\t\tNo turns recorded yet. Per-turn tracking starts when{\" \"}\n\t\t\t\t\t<code>MEGACOMPACT_TURNS_DB=1</code> and the session runs through a\n\t\t\t\t\tcompaction. Each turn records its context metrics + the checkpoints\n\t\t\t\t\trecalled into it.\n\t\t\t\t</p>\n\t\t\t</div>\n\t\t);\n\t}\n\n\treturn (\n\t\t<div className=\"flex flex-col gap-4\">\n\t\t\t<h3 className=\"font-heading text-lg font-semibold\">\n\t\t\t\tTurn-by-turn memory tracking + recall\n\t\t\t</h3>\n\t\t\t{notice && <Badge variant=\"warning\">{notice}</Badge>}\n\n\t\t\t{intents && intents.intents.length > 0 && (\n\t\t\t\t<Card>\n\t\t\t\t\t<CardHeader>\n\t\t\t\t\t\t<CardTitle>\n\t\t\t\t\t\t\tPending rewind intents ({intents.intents.length})\n\t\t\t\t\t\t</CardTitle>\n\t\t\t\t\t</CardHeader>\n\t\t\t\t\t<CardContent>\n\t\t\t\t\t\t<ul className=\"list-disc space-y-1 pl-5\">\n\t\t\t\t\t\t\t{intents.intents.map((i) => (\n\t\t\t\t\t\t\t\t<li key={i.id}>\n\t\t\t\t\t\t\t\t\t{fmtTs(i.createdAt)} — rewind{\" \"}\n\t\t\t\t\t\t\t\t\t<code>{i.conversationId}</code> to turn {i.targetTurnIndex}\n\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</ul>\n\t\t\t\t\t</CardContent>\n\t\t\t\t</Card>\n\t\t\t)}\n\n\t\t\t<Card>\n\t\t\t\t<CardHeader>\n\t\t\t\t\t<CardTitle>\n\t\t\t\t\t\tConversations ({turns.conversations.length})\n\t\t\t\t\t</CardTitle>\n\t\t\t\t</CardHeader>\n\t\t\t\t<CardContent>\n\t\t\t\t\t<table className=\"w-full border-collapse text-sm\">\n\t\t\t\t\t\t<thead>\n\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t\t<th className=\"border-b border-border px-3 py-2 text-left font-medium text-muted-foreground\">Conversation</th>\n\t\t\t\t\t\t\t\t<th className=\"border-b border-border px-3 py-2 text-left font-medium text-muted-foreground\">Turns</th>\n\t\t\t\t\t\t\t\t<th className=\"border-b border-border px-3 py-2 text-left font-medium text-muted-foreground\">Recall</th>\n\t\t\t\t\t\t\t\t<th className=\"border-b border-border px-3 py-2 text-left font-medium text-muted-foreground\">Epochs</th>\n\t\t\t\t\t\t\t\t<th className=\"border-b border-border px-3 py-2 text-left font-medium text-muted-foreground\">Avg ctx%</th>\n\t\t\t\t\t\t\t\t<th className=\"border-b border-border px-3 py-2 text-left font-medium text-muted-foreground\">Last turn</th>\n\t\t\t\t\t\t\t\t<th className=\"border-b border-border px-3 py-2\"></th>\n\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t</thead>\n\t\t\t\t\t\t<tbody>\n\t\t\t\t\t\t\t{turns.conversations.map((c: ConversationSummary) => (\n\t\t\t\t\t\t\t\t<ConversationRow\n\t\t\t\t\t\t\t\t\tkey={c.conversationId}\n\t\t\t\t\t\t\t\t\tc={c}\n\t\t\t\t\t\t\t\t\tactive={turns.activeConversationId === c.conversationId}\n\t\t\t\t\t\t\t\t\texpanded={expanded === c.conversationId}\n\t\t\t\t\t\t\t\t\tonExpand={() => expand(c.conversationId)}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</tbody>\n\t\t\t\t\t</table>\n\t\t\t\t</CardContent>\n\t\t\t</Card>\n\n\t\t\t{expanded && (\n\t\t\t\t<Card>\n\t\t\t\t\t<CardHeader>\n\t\t\t\t\t\t<CardTitle>\n\t\t\t\t\t\t\tTurns in <code>{expanded}</code>\n\t\t\t\t\t\t</CardTitle>\n\t\t\t\t\t</CardHeader>\n\t\t\t\t\t<CardContent>\n\t\t\t\t\t\t{detailLoading ? (\n\t\t\t\t\t\t\t<div className=\"text-sm text-muted-foreground\">Loading turns…</div>\n\t\t\t\t\t\t) : detail ? (\n\t\t\t\t\t\t\t<TurnDetail\n\t\t\t\t\t\t\t\tdetail={detail}\n\t\t\t\t\t\t\t\tbusy={busy}\n\t\t\t\t\t\t\t\tonFork={onFork}\n\t\t\t\t\t\t\t\tonRewind={onRewind}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t<div className=\"text-sm text-muted-foreground\">No detail.</div>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</CardContent>\n\t\t\t\t</Card>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n\nfunction ConversationRow({\n\tc,\n\tactive,\n\texpanded,\n\tonExpand,\n}: {\n\tc: ConversationSummary;\n\tactive: boolean;\n\texpanded: boolean;\n\tonExpand: () => void;\n}): React.ReactElement {\n\treturn (\n\t\t<>\n\t\t\t<tr\n\t\t\t\tclassName={`border-b border-border/50 hover:bg-bg-elevated/40 ${\n\t\t\t\t\tactive ? \"bg-primary/10\" : \"\"\n\t\t\t\t}`}\n\t\t\t\tonClick={onExpand}\n\t\t\t\tstyle={{ cursor: \"pointer\" }}\n\t\t\t>\n\t\t\t\t<td className=\"px-3 py-2\">\n\t\t\t\t\t{expanded ? \"▾\" : \"▸\"} <code>{c.conversationId}</code>\n\t\t\t\t\t{active && <Badge variant=\"accent\" className=\"ml-2\">active</Badge>}\n\t\t\t\t</td>\n\t\t\t\t<td className=\"px-3 py-2 text-right\">{c.turnCount}</td>\n\t\t\t\t<td className=\"px-3 py-2 text-right\">{c.totalRecall}</td>\n\t\t\t\t<td className=\"px-3 py-2 text-right\">{c.epochCount}</td>\n\t\t\t\t<td className=\"px-3 py-2 text-right\">{c.avgCtxPercent.toFixed(0)}%</td>\n\t\t\t\t<td className=\"px-3 py-2\">{fmtTs(c.lastTurnAt)}</td>\n\t\t\t\t<td className=\"px-3 py-2\" />\n\t\t\t</tr>\n\t\t</>\n\t);\n}\n\nfunction TurnDetail({\n\tdetail,\n\tbusy,\n\tonFork,\n\tonRewind,\n}: {\n\tdetail: ConversationTurnsResponse;\n\tbusy: string | null;\n\tonFork: (conversationId: string, turnIndex: number) => void;\n\tonRewind: (conversationId: string, turnIndex: number) => void;\n}): React.ReactElement {\n\treturn (\n\t\t<table className=\"w-full border-collapse text-sm\">\n\t\t\t<thead>\n\t\t\t\t<tr>\n\t\t\t\t\t<th className=\"border-b border-border px-3 py-2 text-left font-medium text-muted-foreground\">#</th>\n\t\t\t\t\t<th className=\"border-b border-border px-3 py-2 text-left font-medium text-muted-foreground\">Role</th>\n\t\t\t\t\t<th className=\"border-b border-border px-3 py-2 text-left font-medium text-muted-foreground\">Ctx</th>\n\t\t\t\t\t<th className=\"border-b border-border px-3 py-2 text-left font-medium text-muted-foreground\">Band</th>\n\t\t\t\t\t<th className=\"border-b border-border px-3 py-2 text-left font-medium text-muted-foreground\">Epoch</th>\n\t\t\t\t\t<th className=\"border-b border-border px-3 py-2 text-left font-medium text-muted-foreground\">Recalled checkpoints (memory recall)</th>\n\t\t\t\t\t<th className=\"border-b border-border px-3 py-2 text-left font-medium text-muted-foreground\">HyDE</th>\n\t\t\t\t\t<th className=\"border-b border-border px-3 py-2 text-left font-medium text-muted-foreground\">Ended</th>\n\t\t\t\t\t<th className=\"border-b border-border px-3 py-2 text-left font-medium text-muted-foreground\">Actions</th>\n\t\t\t\t</tr>\n\t\t\t</thead>\n\t\t\t<tbody>\n\t\t\t\t{detail.turns.map((t) => (\n\t\t\t\t\t<tr\n\t\t\t\t\t\tkey={`${t.conversationId}:${t.turnIndex}`}\n\t\t\t\t\t\tclassName=\"border-b border-border/50 hover:bg-bg-elevated/40\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<td className=\"px-3 py-2 text-right\">{t.turnIndex}</td>\n\t\t\t\t\t\t<td className=\"px-3 py-2\">{t.role}</td>\n\t\t\t\t\t\t<td className=\"px-3 py-2\">\n\t\t\t\t\t\t\t{t.ctxTokens ?? \"—\"}\n\t\t\t\t\t\t\t{t.ctxPercent != null && (\n\t\t\t\t\t\t\t\t<span className=\"text-muted-foreground\">\n\t\t\t\t\t\t\t\t\t{\" \"}\n\t\t\t\t\t\t\t\t\t· {t.ctxPercent.toFixed(0)}%\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</td>\n\t\t\t\t\t\t<td className=\"px-3 py-2\">\n\t\t\t\t\t\t\t<Badge variant={bandVariant(t.pressureBand)}>\n\t\t\t\t\t\t\t\t{t.pressureBand ?? \"—\"}\n\t\t\t\t\t\t\t</Badge>\n\t\t\t\t\t\t</td>\n\t\t\t\t\t\t<td className=\"px-3 py-2\">{t.epochId ? <code>{t.epochId.slice(0, 8)}</code> : \"—\"}</td>\n\t\t\t\t\t\t<td className=\"px-3 py-2\">\n\t\t\t\t\t\t\t{t.recall.length === 0 ? (\n\t\t\t\t\t\t\t\t<span className=\"text-muted-foreground\">—</span>\n\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t<ul className=\"space-y-1\">\n\t\t\t\t\t\t\t\t\t{t.recall.map((r) => (\n\t\t\t\t\t\t\t\t\t\t<li key={r.checkpointId}>\n\t\t\t\t\t\t\t\t\t\t\t<code>{r.checkpointId.slice(0, 12)}</code>{\" \"}\n\t\t\t\t\t\t\t\t\t\t\t<span className=\"text-muted-foreground\">\n\t\t\t\t\t\t\t\t\t\t\t\t{sourceLabel(r.source)} · {r.score.toFixed(2)}\n\t\t\t\t\t\t\t\t\t\t\t\t{r.raptorLevel != null ? ` · L${r.raptorLevel}` : \"\"}\n\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t\t</ul>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</td>\n\t\t\t\t\t\t<td className=\"px-3 py-2 align-top\">\n\t\t\t\t\t\t\t<HydeDetailPanel\n\t\t\t\t\t\t\t\tconversationId={t.conversationId}\n\t\t\t\t\t\t\t\tturnIndex={t.turnIndex}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</td>\n\t\t\t\t\t\t<td className=\"px-3 py-2\">{fmtTs(t.endedAt)}</td>\n\t\t\t\t\t\t<td className=\"px-3 py-2\">\n\t\t\t\t\t\t\t<div className=\"flex gap-2\">\n\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\t\t\t\tdisabled={busy !== null}\n\t\t\t\t\t\t\t\t\tonClick={() => onFork(t.conversationId, t.turnIndex)}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\tfork\n\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\t\t\t\tdisabled={busy !== null}\n\t\t\t\t\t\t\t\t\tonClick={() => onRewind(t.conversationId, t.turnIndex)}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\trewind\n\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</td>\n\t\t\t\t\t</tr>\n\t\t\t\t))}\n\t\t\t</tbody>\n\t\t</table>\n\t);\n}\n"],"names":["rowKey","conversationId","turnIndex","truncateDoc","doc","maxLines","lines","ScoreLine","row","jsxs","jsx","Badge","HydeDetailPanel","metrics","useApi","useMemo","fetchRagMetrics","showFullDoc","setShowFullDoc","useState","t","showDoc","visibleDoc","Card","Fragment","Arrow","v","skipReason","fmtTs","ms","bandVariant","band","sourceLabel","s","TurnsTab","turns","loading","error","useCallback","fetchTurns","intents","fetchTurnIntents","expanded","setExpanded","detail","setDetail","detailLoading","setDetailLoading","busy","setBusy","notice","setNotice","expand","d","fetchConversationTurns","onFork","out","postFork","e","onRewind","postTurnIntent","CardHeader","CardTitle","CardContent","i","c","ConversationRow","TurnDetail","active","onExpand","r","Button"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"TurnsTab-DBMgmvnX.js","sources":["../../src/components/HydeDetailPanel.tsx","../../src/tabs/TurnsTab.tsx"],"sourcesContent":["/**\n * dashboard-client/src/components/HydeDetailPanel.tsx — per-turn HyDE + recall\n * quality detail (H3.1).\n *\n * Fetches /api/rag-metrics once and indexes telemetry rows by the composite\n * `conversationId:turnIndex` key (the dashboard TurnRow has no DB id, so the\n * pair is the stable identifier). Renders one of three HyDE states:\n * - HyDE ran: hypothetical doc (collapsible), raw/hyde/fused hit counts,\n * lift multiplier, generation latency.\n * - HyDE skipped: reason (disabled / no-llm / generation-failed).\n * - No telemetry: nothing (the parent only renders us when flagged).\n * Plus a CRAG-style mini score line when recall metrics exist.\n *\n * PREVENT-PI-004: relative-path fetch to the same-origin dashboard server.\n */\n\nimport type React from \"react\";\nimport { useMemo, useState } from \"react\";\nimport { useApi } from \"../hooks/useApi\";\nimport { fetchRagMetrics } from \"../api/client\";\nimport type { RagMetricsResponse } from \"@contracts\";\nimport { Card } from \"./ui/card\";\nimport { Badge } from \"./ui/badge\";\n\n/** Per-turn telemetry row shape derived from the rag-metrics contract. */\ntype TelemetryRow = RagMetricsResponse[\"recent\"][number];\n\nexport interface HydeDetailPanelProps {\n\tconversationId: string;\n\tturnIndex: number;\n}\n\n/** Composite key matching a telemetry row to a rendered turn. */\nfunction rowKey(conversationId: string, turnIndex: number): string {\n\treturn `${conversationId}:${turnIndex}`;\n}\n\n/** First N lines of the hypothetical doc (or full when expanded). */\nfunction truncateDoc(doc: string, maxLines: number): string {\n\tconst lines = doc.split(\"\\n\");\n\treturn lines.length > maxLines ? lines.slice(0, maxLines).join(\"\\n\") : doc;\n}\n\n/** CRAG-style mini score line when recall metrics were captured. */\nfunction ScoreLine({ row }: { row: TelemetryRow }): React.ReactElement {\n\tconst hasScore = row.recallScore > 0 || row.recallPass === 1;\n\tif (!hasScore) {\n\t\treturn (\n\t\t\t<div className=\"mt-2 text-xs text-muted-foreground\">\n\t\t\t\tNo recall-quality metrics for this turn.\n\t\t\t</div>\n\t\t);\n\t}\n\treturn (\n\t\t<div className=\"mt-2 rounded-md border border-border/60 bg-bg-elevated/40 p-2\">\n\t\t\t<div className=\"flex items-center gap-2 text-xs\">\n\t\t\t\t<span className=\"font-medium text-muted-foreground\">Recall quality</span>\n\t\t\t\t<Badge variant={row.recallPass === 1 ? \"success\" : \"warning\"}>\n\t\t\t\t\t{row.recallPass === 1 ? \"pass\" : \"fail\"}\n\t\t\t\t</Badge>\n\t\t\t\t<span>\n\t\t\t\t\tscore <strong className=\"text-foreground\">{row.recallScore.toFixed(2)}</strong>\n\t\t\t\t</span>\n\t\t\t</div>\n\t\t\t<div className=\"mt-1 flex flex-wrap gap-x-4 gap-y-0.5 text-xs text-muted-foreground\">\n\t\t\t\t<span>relevance {row.recallRelevance.toFixed(2)}</span>\n\t\t\t\t<span>coverage {row.recallCoverage.toFixed(2)}</span>\n\t\t\t\t<span>diversity {row.recallDiversity.toFixed(2)}</span>\n\t\t\t\t<span>specificity {row.recallSpecificity.toFixed(2)}</span>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nexport const HydeDetailPanel: React.FC<HydeDetailPanelProps> = ({\n\tconversationId,\n\tturnIndex,\n}) => {\n\tconst { data: metrics } = useApi<RagMetricsResponse>(\n\t\tuseMemo(() => () => fetchRagMetrics(), []),\n\t\t{ pollInterval: 30_000 },\n\t);\n\tconst [showFullDoc, setShowFullDoc] = useState(false);\n\n\t// Rebuild the index whenever metrics arrive; rare enough that a full\n\t// rebuild on each render is cheaper than memoizing with a mutable dep.\n\tconst row = useMemo(\n\t\t() =>\n\t\t\tmetrics?.recent.find(\n\t\t\t\t(t) => rowKey(t.conversationId, t.turnIndex) === rowKey(conversationId, turnIndex),\n\t\t\t) ?? null,\n\t\t[metrics, conversationId, turnIndex],\n\t);\n\n\tif (!metrics) {\n\t\treturn <div className=\"text-xs text-muted-foreground\">Loading HyDE stats…</div>;\n\t}\n\tif (!row) {\n\t\treturn (\n\t\t\t<div className=\"text-xs text-muted-foreground\">\n\t\t\t\tNo HyDE/recall telemetry recorded for this turn.\n\t\t\t</div>\n\t\t);\n\t}\n\n\tconst doc = row.hydeDoc ?? \"\";\n\tconst docLines = doc.split(\"\\n\").length;\n\tconst showDoc = docLines > 3;\n\tconst visibleDoc = showFullDoc || !showDoc ? doc : truncateDoc(doc, 3);\n\n\treturn (\n\t\t<Card className=\"border border-border/60 bg-bg-elevated/20 p-3\">\n\t\t\t{row.hydeRan === 1 ? (\n\t\t\t\t<>\n\t\t\t\t\t<div className=\"mb-2 flex flex-wrap items-center gap-2 text-xs\">\n\t\t\t\t\t\t<Badge variant=\"accent\">HyDE ran</Badge>\n\t\t\t\t\t\t<span className=\"text-muted-foreground\">\n\t\t\t\t\t\t\tgen <strong className=\"text-foreground\">{row.hydeGenerationMs}ms</strong>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span className=\"text-muted-foreground\">\n\t\t\t\t\t\t\traw <strong className=\"text-foreground\">{row.hydeRawCount}</strong>\n\t\t\t\t\t\t\t<Arrow />\n\t\t\t\t\t\t\thyde <strong className=\"text-foreground\">{row.hydeHydeCount}</strong>\n\t\t\t\t\t\t\t<Arrow />\n\t\t\t\t\t\t\tfused <strong className=\"text-foreground\">{row.hydeFusedCount}</strong>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span className=\"text-muted-foreground\">\n\t\t\t\t\t\t\tlift{\" \"}\n\t\t\t\t\t\t\t<strong className=\"text-neon\">{row.hydeLift.toFixed(2)}×</strong>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t{doc && (\n\t\t\t\t\t\t<pre className=\"max-h-40 overflow-auto whitespace-pre-wrap rounded-md border border-border/50 bg-bg-elevated/40 p-2 font-mono text-xs text-muted-foreground\">\n\t\t\t\t\t\t\t{visibleDoc}\n\t\t\t\t\t\t</pre>\n\t\t\t\t\t)}\n\t\t\t\t\t{showDoc && (\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\tclassName=\"mt-1 text-xs text-accent underline\"\n\t\t\t\t\t\t\tonClick={() => setShowFullDoc((v) => !v)}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{showFullDoc ? \"Show less\" : \"Show full hypothetical doc\"}\n\t\t\t\t\t\t</button>\n\t\t\t\t\t)}\n\t\t\t\t</>\n\t\t\t) : (\n\t\t\t\t<div className=\"flex items-center gap-2 text-xs\">\n\t\t\t\t\t<Badge variant=\"warning\">HyDE skipped</Badge>\n\t\t\t\t\t<span className=\"text-muted-foreground\">{skipReason(row)}</span>\n\t\t\t\t</div>\n\t\t\t)}\n\t\t\t<ScoreLine row={row} />\n\t\t</Card>\n\t);\n};\n\nfunction Arrow(): React.ReactElement {\n\treturn <span className=\"text-muted-foreground/60\"> → </span>;\n}\n\nfunction skipReason(row: TelemetryRow): string {\n\tswitch (row.hydeReason) {\n\t\tcase \"no-llm\":\n\t\t\treturn \"No LLM embedder available (requires Ollama/HTTP embedder).\";\n\t\tcase \"generation-failed\":\n\t\t\treturn \"Hypothetical doc generation failed this turn.\";\n\t\tcase \"disabled\":\n\t\t\treturn \"HyDE is disabled (MEGACOMPACT_HYDE_DISABLED).\";\n\t\tdefault:\n\t\t\treturn \"HyDE was not invoked for this turn.\";\n\t}\n}\n","/**\n * dashboard-client/src/tabs/TurnsTab.tsx — Turn-by-turn memory tracking + recall (S52).\n *\n * Fetches /api/turns (conversation list), expands the active conversation into\n * per-turn detail (/api/turns/conversation/:id), and renders:\n * - Conversation summary list (turn count, recall total, epochs, last turn).\n * - Per-turn rows: turn index, role, ctx tokens/percent + pressure band,\n * epoch id, and the injected-checkpoint recall set (the memory recalled\n * at each turn, with score + source).\n * - Pending rewind intents (S52A) + Fork action (S50C primitive).\n *\n * Read-mostly via /api/turns; fork/intent/prune via POST. No absolute URLs\n * (PREVENT-PI-004: loopback-only, same-origin static bundle).\n */\n\nimport type React from \"react\";\nimport { useCallback, useState } from \"react\";\nimport { useApi } from \"../hooks/useApi\";\nimport {\n\tfetchTurns,\n\tfetchConversationTurns,\n\tfetchTurnIntents,\n\tpostFork,\n\tpostTurnIntent,\n} from \"../api/client\";\nimport type {\n\tTurnsResponse,\n\tConversationTurnsResponse,\n\tRewindIntentsResponse,\n\tTurnRow,\n\tConversationSummary,\n} from \"@contracts\";\nimport { Card, CardContent, CardHeader, CardTitle } from \"../components/ui/card\";\nimport { Badge } from \"../components/ui/badge\";\nimport { Button } from \"../components/ui/button\";\nimport { HydeDetailPanel } from \"../components/HydeDetailPanel\";\n\nfunction fmtTs(ms: number): string {\n\tif (!ms) return \"—\";\n\treturn new Date(ms).toLocaleString();\n}\n\nfunction bandVariant(band: TurnRow[\"pressureBand\"]): \"success\" | \"warning\" | \"danger\" | \"default\" {\n\tif (band === \"green\") return \"success\";\n\tif (band === \"yellow\") return \"warning\";\n\tif (band === \"red\") return \"danger\";\n\treturn \"default\";\n}\n\nfunction sourceLabel(s: TurnRow[\"recall\"][number][\"source\"]): string {\n\tif (s === \"checkpoint\") return \"ckpt\";\n\tif (s === \"cluster_summary\") return \"raptor\";\n\treturn \"mem\";\n}\n\nexport default function TurnsTab(): React.ReactElement {\n\tconst {\n\t\tdata: turns,\n\t\tloading,\n\t\terror,\n\t} = useApi<TurnsResponse>(\n\t\tuseCallback(() => fetchTurns(), []),\n\t\t{\n\t\t\tpollInterval: 10_000,\n\t\t},\n\t);\n\tconst { data: intents } = useApi<RewindIntentsResponse>(\n\t\tuseCallback(() => fetchTurnIntents(), []),\n\t\t{ pollInterval: 5_000 },\n\t);\n\tconst [expanded, setExpanded] = useState<string | null>(null);\n\tconst [detail, setDetail] = useState<ConversationTurnsResponse | null>(null);\n\tconst [detailLoading, setDetailLoading] = useState(false);\n\tconst [busy, setBusy] = useState<string | null>(null);\n\tconst [notice, setNotice] = useState<string | null>(null);\n\n\tconst expand = useCallback(\n\t\tasync (conversationId: string) => {\n\t\t\tif (expanded === conversationId) {\n\t\t\t\tsetExpanded(null);\n\t\t\t\tsetDetail(null);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tsetExpanded(conversationId);\n\t\t\tsetDetailLoading(true);\n\t\t\tsetDetail(null);\n\t\t\ttry {\n\t\t\t\tconst d = await fetchConversationTurns(conversationId);\n\t\t\t\tsetDetail(d);\n\t\t\t} finally {\n\t\t\t\tsetDetailLoading(false);\n\t\t\t}\n\t\t},\n\t\t[expanded],\n\t);\n\n\tconst onFork = useCallback(\n\t\tasync (conversationId: string, turnIndex: number) => {\n\t\t\tsetBusy(`fork:${conversationId}:${turnIndex}`);\n\t\t\tsetNotice(null);\n\t\t\ttry {\n\t\t\t\tconst out = await postFork(conversationId, turnIndex);\n\t\t\t\tsetNotice(\n\t\t\t\t\t`Forked → ${out.childConversationId} (${out.recalledCount} recalled)`,\n\t\t\t\t);\n\t\t\t} catch (e) {\n\t\t\t\tsetNotice(`Fork failed: ${(e as Error).message}`);\n\t\t\t} finally {\n\t\t\t\tsetBusy(null);\n\t\t\t}\n\t\t},\n\t\t[],\n\t);\n\n\tconst onRewind = useCallback(\n\t\tasync (conversationId: string, turnIndex: number) => {\n\t\t\tsetBusy(`rewind:${conversationId}:${turnIndex}`);\n\t\t\tsetNotice(null);\n\t\t\ttry {\n\t\t\t\tawait postTurnIntent(conversationId, turnIndex);\n\t\t\t\tsetNotice(`Rewind intent queued for turn ${turnIndex}`);\n\t\t\t} catch (e) {\n\t\t\t\tsetNotice(`Rewind failed: ${(e as Error).message}`);\n\t\t\t} finally {\n\t\t\t\tsetBusy(null);\n\t\t\t}\n\t\t},\n\t\t[],\n\t);\n\n\tif (error && !turns) {\n\t\treturn <div className=\"tab-stub\">Error loading turns: {error.message}</div>;\n\t}\n\tif (loading && !turns) {\n\t\treturn <div className=\"tab-stub\">Loading turns…</div>;\n\t}\n\tif (!turns || turns.conversations.length === 0) {\n\t\treturn (\n\t\t\t<div className=\"tab-stub\">\n\t\t\t\t<h3>Turn-by-turn memory</h3>\n\t\t\t\t<p>\n\t\t\t\t\tNo turns recorded yet. Per-turn tracking starts when{\" \"}\n\t\t\t\t\t<code>MEGACOMPACT_TURNS_DB=1</code> and the session runs through a\n\t\t\t\t\tcompaction. Each turn records its context metrics + the checkpoints\n\t\t\t\t\trecalled into it.\n\t\t\t\t</p>\n\t\t\t</div>\n\t\t);\n\t}\n\n\treturn (\n\t\t<div className=\"flex flex-col gap-4\">\n\t\t\t<h3 className=\"font-heading text-lg font-semibold\">\n\t\t\t\tTurn-by-turn memory tracking + recall\n\t\t\t</h3>\n\t\t\t{notice && <Badge variant=\"warning\">{notice}</Badge>}\n\n\t\t\t{intents && intents.intents.length > 0 && (\n\t\t\t\t<Card>\n\t\t\t\t\t<CardHeader>\n\t\t\t\t\t\t<CardTitle>\n\t\t\t\t\t\t\tPending rewind intents ({intents.intents.length})\n\t\t\t\t\t\t</CardTitle>\n\t\t\t\t\t</CardHeader>\n\t\t\t\t\t<CardContent>\n\t\t\t\t\t\t<ul className=\"list-disc space-y-1 pl-5\">\n\t\t\t\t\t\t\t{intents.intents.map((i) => (\n\t\t\t\t\t\t\t\t<li key={i.id}>\n\t\t\t\t\t\t\t\t\t{fmtTs(i.createdAt)} — rewind{\" \"}\n\t\t\t\t\t\t\t\t\t<code>{i.conversationId}</code> to turn {i.targetTurnIndex}\n\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</ul>\n\t\t\t\t\t</CardContent>\n\t\t\t\t</Card>\n\t\t\t)}\n\n\t\t\t<Card>\n\t\t\t\t<CardHeader>\n\t\t\t\t\t<CardTitle>\n\t\t\t\t\t\tConversations ({turns.conversations.length})\n\t\t\t\t\t</CardTitle>\n\t\t\t\t</CardHeader>\n\t\t\t\t<CardContent>\n\t\t\t\t\t<table className=\"w-full border-collapse text-sm\">\n\t\t\t\t\t\t<thead>\n\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t\t<th className=\"border-b border-border px-3 py-2 text-left font-medium text-muted-foreground\">Conversation</th>\n\t\t\t\t\t\t\t\t<th className=\"border-b border-border px-3 py-2 text-left font-medium text-muted-foreground\">Turns</th>\n\t\t\t\t\t\t\t\t<th className=\"border-b border-border px-3 py-2 text-left font-medium text-muted-foreground\">Recall</th>\n\t\t\t\t\t\t\t\t<th className=\"border-b border-border px-3 py-2 text-left font-medium text-muted-foreground\">Epochs</th>\n\t\t\t\t\t\t\t\t<th className=\"border-b border-border px-3 py-2 text-left font-medium text-muted-foreground\">Avg ctx%</th>\n\t\t\t\t\t\t\t\t<th className=\"border-b border-border px-3 py-2 text-left font-medium text-muted-foreground\">Last turn</th>\n\t\t\t\t\t\t\t\t<th className=\"border-b border-border px-3 py-2\"></th>\n\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t</thead>\n\t\t\t\t\t\t<tbody>\n\t\t\t\t\t\t\t{turns.conversations.map((c: ConversationSummary) => (\n\t\t\t\t\t\t\t\t<ConversationRow\n\t\t\t\t\t\t\t\t\tkey={c.conversationId}\n\t\t\t\t\t\t\t\t\tc={c}\n\t\t\t\t\t\t\t\t\tactive={turns.activeConversationId === c.conversationId}\n\t\t\t\t\t\t\t\t\texpanded={expanded === c.conversationId}\n\t\t\t\t\t\t\t\t\tonExpand={() => expand(c.conversationId)}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</tbody>\n\t\t\t\t\t</table>\n\t\t\t\t</CardContent>\n\t\t\t</Card>\n\n\t\t\t{expanded && (\n\t\t\t\t<Card>\n\t\t\t\t\t<CardHeader>\n\t\t\t\t\t\t<CardTitle>\n\t\t\t\t\t\t\tTurns in <code>{expanded}</code>\n\t\t\t\t\t\t</CardTitle>\n\t\t\t\t\t</CardHeader>\n\t\t\t\t\t<CardContent>\n\t\t\t\t\t\t{detailLoading ? (\n\t\t\t\t\t\t\t<div className=\"text-sm text-muted-foreground\">Loading turns…</div>\n\t\t\t\t\t\t) : detail ? (\n\t\t\t\t\t\t\t<TurnDetail\n\t\t\t\t\t\t\t\tdetail={detail}\n\t\t\t\t\t\t\t\tbusy={busy}\n\t\t\t\t\t\t\t\tonFork={onFork}\n\t\t\t\t\t\t\t\tonRewind={onRewind}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t<div className=\"text-sm text-muted-foreground\">No detail.</div>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</CardContent>\n\t\t\t\t</Card>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n\nfunction ConversationRow({\n\tc,\n\tactive,\n\texpanded,\n\tonExpand,\n}: {\n\tc: ConversationSummary;\n\tactive: boolean;\n\texpanded: boolean;\n\tonExpand: () => void;\n}): React.ReactElement {\n\treturn (\n\t\t<>\n\t\t\t<tr\n\t\t\t\tclassName={`border-b border-border/50 hover:bg-bg-elevated/40 ${\n\t\t\t\t\tactive ? \"bg-primary/10\" : \"\"\n\t\t\t\t}`}\n\t\t\t\tonClick={onExpand}\n\t\t\t\tstyle={{ cursor: \"pointer\" }}\n\t\t\t>\n\t\t\t\t<td className=\"px-3 py-2\">\n\t\t\t\t\t{expanded ? \"▾\" : \"▸\"} <code>{c.conversationId}</code>\n\t\t\t\t\t{active && <Badge variant=\"accent\" className=\"ml-2\">active</Badge>}\n\t\t\t\t</td>\n\t\t\t\t<td className=\"px-3 py-2 text-right\">{c.turnCount}</td>\n\t\t\t\t<td className=\"px-3 py-2 text-right\">{c.totalRecall}</td>\n\t\t\t\t<td className=\"px-3 py-2 text-right\">{c.epochCount}</td>\n\t\t\t\t<td className=\"px-3 py-2 text-right\">{c.avgCtxPercent.toFixed(0)}%</td>\n\t\t\t\t<td className=\"px-3 py-2\">{fmtTs(c.lastTurnAt)}</td>\n\t\t\t\t<td className=\"px-3 py-2\" />\n\t\t\t</tr>\n\t\t</>\n\t);\n}\n\nfunction TurnDetail({\n\tdetail,\n\tbusy,\n\tonFork,\n\tonRewind,\n}: {\n\tdetail: ConversationTurnsResponse;\n\tbusy: string | null;\n\tonFork: (conversationId: string, turnIndex: number) => void;\n\tonRewind: (conversationId: string, turnIndex: number) => void;\n}): React.ReactElement {\n\treturn (\n\t\t<table className=\"w-full border-collapse text-sm\">\n\t\t\t<thead>\n\t\t\t\t<tr>\n\t\t\t\t\t<th className=\"border-b border-border px-3 py-2 text-left font-medium text-muted-foreground\">#</th>\n\t\t\t\t\t<th className=\"border-b border-border px-3 py-2 text-left font-medium text-muted-foreground\">Role</th>\n\t\t\t\t\t<th className=\"border-b border-border px-3 py-2 text-left font-medium text-muted-foreground\">Ctx</th>\n\t\t\t\t\t<th className=\"border-b border-border px-3 py-2 text-left font-medium text-muted-foreground\">Band</th>\n\t\t\t\t\t<th className=\"border-b border-border px-3 py-2 text-left font-medium text-muted-foreground\">Epoch</th>\n\t\t\t\t\t<th className=\"border-b border-border px-3 py-2 text-left font-medium text-muted-foreground\">Recalled checkpoints (memory recall)</th>\n\t\t\t\t\t<th className=\"border-b border-border px-3 py-2 text-left font-medium text-muted-foreground\">HyDE</th>\n\t\t\t\t\t<th className=\"border-b border-border px-3 py-2 text-left font-medium text-muted-foreground\">Ended</th>\n\t\t\t\t\t<th className=\"border-b border-border px-3 py-2 text-left font-medium text-muted-foreground\">Actions</th>\n\t\t\t\t</tr>\n\t\t\t</thead>\n\t\t\t<tbody>\n\t\t\t\t{detail.turns.map((t) => (\n\t\t\t\t\t<tr\n\t\t\t\t\t\tkey={`${t.conversationId}:${t.turnIndex}`}\n\t\t\t\t\t\tclassName=\"border-b border-border/50 hover:bg-bg-elevated/40\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<td className=\"px-3 py-2 text-right\">{t.turnIndex}</td>\n\t\t\t\t\t\t<td className=\"px-3 py-2\">{t.role}</td>\n\t\t\t\t\t\t<td className=\"px-3 py-2\">\n\t\t\t\t\t\t\t{t.ctxTokens ?? \"—\"}\n\t\t\t\t\t\t\t{t.ctxPercent != null && (\n\t\t\t\t\t\t\t\t<span className=\"text-muted-foreground\">\n\t\t\t\t\t\t\t\t\t{\" \"}\n\t\t\t\t\t\t\t\t\t· {t.ctxPercent.toFixed(0)}%\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</td>\n\t\t\t\t\t\t<td className=\"px-3 py-2\">\n\t\t\t\t\t\t\t<Badge variant={bandVariant(t.pressureBand)}>\n\t\t\t\t\t\t\t\t{t.pressureBand ?? \"—\"}\n\t\t\t\t\t\t\t</Badge>\n\t\t\t\t\t\t</td>\n\t\t\t\t\t\t<td className=\"px-3 py-2\">{t.epochId ? <code>{t.epochId.slice(0, 8)}</code> : \"—\"}</td>\n\t\t\t\t\t\t<td className=\"px-3 py-2\">\n\t\t\t\t\t\t\t{t.recall.length === 0 ? (\n\t\t\t\t\t\t\t\t<span className=\"text-muted-foreground\">—</span>\n\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t<ul className=\"space-y-1\">\n\t\t\t\t\t\t\t\t\t{t.recall.map((r) => (\n\t\t\t\t\t\t\t\t\t\t<li key={r.checkpointId}>\n\t\t\t\t\t\t\t\t\t\t\t<code>{r.checkpointId.slice(0, 12)}</code>{\" \"}\n\t\t\t\t\t\t\t\t\t\t\t<span className=\"text-muted-foreground\">\n\t\t\t\t\t\t\t\t\t\t\t\t{sourceLabel(r.source)} · {r.score.toFixed(2)}\n\t\t\t\t\t\t\t\t\t\t\t\t{r.raptorLevel != null ? ` · L${r.raptorLevel}` : \"\"}\n\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t\t</ul>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</td>\n\t\t\t\t\t\t<td className=\"px-3 py-2 align-top\">\n\t\t\t\t\t\t\t<HydeDetailPanel\n\t\t\t\t\t\t\t\tconversationId={t.conversationId}\n\t\t\t\t\t\t\t\tturnIndex={t.turnIndex}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</td>\n\t\t\t\t\t\t<td className=\"px-3 py-2\">{fmtTs(t.endedAt)}</td>\n\t\t\t\t\t\t<td className=\"px-3 py-2\">\n\t\t\t\t\t\t\t<div className=\"flex gap-2\">\n\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\t\t\t\tdisabled={busy !== null}\n\t\t\t\t\t\t\t\t\tonClick={() => onFork(t.conversationId, t.turnIndex)}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\tfork\n\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\t\t\t\tdisabled={busy !== null}\n\t\t\t\t\t\t\t\t\tonClick={() => onRewind(t.conversationId, t.turnIndex)}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\trewind\n\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</td>\n\t\t\t\t\t</tr>\n\t\t\t\t))}\n\t\t\t</tbody>\n\t\t</table>\n\t);\n}\n"],"names":["rowKey","conversationId","turnIndex","truncateDoc","doc","maxLines","lines","ScoreLine","row","jsxs","jsx","Badge","HydeDetailPanel","metrics","useApi","useMemo","fetchRagMetrics","showFullDoc","setShowFullDoc","useState","t","showDoc","visibleDoc","Card","Fragment","Arrow","v","skipReason","fmtTs","ms","bandVariant","band","sourceLabel","s","TurnsTab","turns","loading","error","useCallback","fetchTurns","intents","fetchTurnIntents","expanded","setExpanded","detail","setDetail","detailLoading","setDetailLoading","busy","setBusy","notice","setNotice","expand","d","fetchConversationTurns","onFork","out","postFork","e","onRewind","postTurnIntent","CardHeader","CardTitle","CardContent","i","c","ConversationRow","TurnDetail","active","onExpand","r","Button"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{r as c,j as e,I as K,J as H,K as U,u as w,L as Y,M as J,O as z,B as y,P as Q}from"./index-B93cewiX.js";import{B as v}from"./button-DtEsZTUE.js";import{T as A}from"./toggle-Bo7kJZ4u.js";import{C as N,c as k}from"./card-DcfTmw-4.js";import{u as X}from"./useSSE-zNhb0c3u.js";function Z(t,o){const m=t.length>14?t.slice(0,14)+"…":t;return o?`${m} (${o})`:m}function ee({topicId:t,others:o,members:m,onMutated:i}){const[n,a]=c.useState(null),[d,h]=c.useState(""),[l,u]=c.useState(""),[r,s]=c.useState([]),[p,b]=c.useState(!1),[g,f]=c.useState(null);function j(x){a(x),f(null),h(""),u(""),s([])}async function q(){b(!0),f(null);try{if(n==="rename")await K(t,d);else if(n==="merge"){if(!l){f("Pick a target topic."),b(!1);return}await H(t,l)}else if(n==="split"){if(r.length===0){f("Pick at least one memory."),b(!1);return}await U(t,r)}a(null),i()}catch(x){f(x instanceof Error?x.message:String(x))}finally{b(!1)}}return e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"flex gap-2",children:[e.jsx(v,{variant:"outline",size:"sm",onClick:()=>j("rename"),children:"Rename"}),e.jsx(v,{variant:"outline",size:"sm",onClick:()=>j("merge"),children:"Merge into…"}),e.jsx(v,{variant:"outline",size:"sm",onClick:()=>j("split"),children:"Split"})]}),n&&e.jsx("div",{className:"fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4",onClick:()=>a(null),children:e.jsxs("div",{className:"w-full max-w-md rounded-lg border border-border bg-bg-card p-5 shadow-xl",role:"dialog","aria-modal":"true","aria-label":n,onClick:x=>x.stopPropagation(),children:[e.jsx("h3",{className:"mb-3 font-heading text-base font-semibold text-foreground",children:n==="rename"?"Rename topic":n==="merge"?"Merge into…":"Split topic"}),n==="rename"&&e.jsx("input",{type:"text",value:d,placeholder:"New label (empty = back to auto)","aria-label":"New topic label",autoFocus:!0,onChange:x=>h(x.target.value),className:"w-full rounded-md border border-border bg-bg-elevated/50 px-3 py-2 text-sm outline-none transition-colors focus:border-primary"}),n==="merge"&&e.jsxs("select",{value:l,"aria-label":"Target topic",onChange:x=>u(x.target.value),className:"w-full rounded-md border border-border bg-bg-elevated/50 px-3 py-2 text-sm outline-none transition-colors focus:border-primary",children:[e.jsx("option",{value:"",children:"Pick a target topic…"}),o.map(x=>e.jsx("option",{value:x.id,children:x.label},x.id))]}),n==="split"&&e.jsx("div",{className:"max-h-56 overflow-auto rounded-md border border-border/60",children:m.map(x=>e.jsxs("label",{className:"flex cursor-pointer items-center gap-2 border-b border-border/40 px-3 py-2 text-sm last:border-0 hover:bg-bg-elevated/40",children:[e.jsx("input",{type:"checkbox",checked:r.includes(x.memoryId),onChange:G=>{s(E=>G.target.checked?[...E,x.memoryId]:E.filter(V=>V!==x.memoryId))},className:"accent-primary"}),e.jsx("span",{className:"font-mono text-xs text-muted-foreground",children:Z(x.memoryId,x.method)})]},x.memoryId))}),g&&e.jsx("p",{className:"mt-3 text-sm text-danger",children:g}),e.jsxs("div",{className:"mt-4 flex justify-end gap-2",children:[e.jsx(v,{variant:"ghost",size:"sm",onClick:()=>a(null),children:"Cancel"}),e.jsx(v,{size:"sm",onClick:q,disabled:p,children:p?"…":"Confirm"})]})]})})]})}const L=720,M=56,T=10,R=3;function C(t){return t?new Date(t).toLocaleDateString():"—"}function te({topicId:t}){const{data:o,error:m}=w(c.useCallback(()=>Y(t),[t]),{});if(m)return e.jsx("div",{className:"rounded-lg border border-border bg-bg-card p-4 text-sm text-muted-foreground",children:"Timeline unavailable."});if(!o)return e.jsx("div",{className:"rounded-lg border border-border bg-bg-card p-4 text-sm text-muted-foreground",children:"Loading timeline…"});const{buckets:i,total:n}=o,a=Math.max(...i.map(l=>l.count),1),d=i.length*(T+R),h=Math.min(1,(L-24)/Math.max(d,1));return e.jsxs("div",{className:"rounded-lg border border-border bg-bg-card p-4",children:[e.jsxs("h3",{className:"mb-2 font-heading text-sm font-semibold text-muted-foreground",children:["Memory timeline"," ",e.jsxs("span",{className:"font-normal text-xs",children:["(",n," memory",n!==1?"ies":"y"," · daily buckets)"]})]}),i.length===0?e.jsx("p",{className:"text-sm text-muted-foreground",children:"No timeline data yet."}):e.jsxs(e.Fragment,{children:[e.jsx("svg",{width:"100%",height:M,viewBox:`0 0 ${Math.max(L,d*h)} ${M}`,role:"img","aria-label":"Memory additions over time",children:i.map((l,u)=>{const r=u*(T+R)*h,s=Math.max(3,l.count/a*(M-20));return e.jsx("rect",{x:r,y:M-6-s,width:Math.max(2,T*h),height:s,rx:2,fill:"hsl(var(--primary))",opacity:.85,children:e.jsx("title",{children:`${C(l.bucket)}: ${l.count}`})},`${l.bucket}-${u}`)})}),e.jsxs("p",{className:"mt-1 text-xs text-muted-foreground",children:["First display: ",C(i[0].bucket)," · last:",C(i[i.length-1].bucket)]})]})]})}function B(t){return t?new Date(t).toLocaleString():"—"}const re={"kmeans+tfidf":"kmeans",merge:"merge",split:"split",manual:"manual"};function se({topicId:t,onBack:o}){const[m,i]=c.useState(0),n=c.useCallback(()=>i(s=>s+1),[]),{data:a,error:d,loading:h}=w(c.useCallback(()=>J(t),[t,m]),{}),{data:l}=w(c.useCallback(()=>z(),[]),{}),u=c.useMemo(()=>{const s=new Map;for(const p of l?.topics??[])s.set(p.id,p.label);return s},[l]);if(d&&!a)return e.jsxs("div",{className:"rounded-lg border border-border bg-bg-card p-6 text-sm text-danger",children:["Error loading topic: ",d.message]});if(h&&!a)return e.jsx("div",{className:"rounded-lg border border-border bg-bg-card p-6 text-sm text-muted-foreground",children:"Loading topic…"});if(!a)return e.jsx("div",{className:"rounded-lg border border-border bg-bg-card p-6 text-sm text-muted-foreground",children:"No topic data available."});const r=(l?.topics??[]).filter(s=>s.id!==t).map(s=>({id:s.id,label:s.label}));return e.jsxs("div",{className:"flex flex-col gap-4",children:[e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(v,{variant:"outline",size:"sm",onClick:o,children:"← Wiki"}),e.jsx("h2",{className:"font-heading text-lg font-semibold",children:a.topic.label}),a.topic.edited&&e.jsx(y,{variant:"outline",children:"edited"}),e.jsxs(y,{variant:"default",children:[a.provenance.length," memories"]})]}),e.jsx(ee,{topicId:t,others:r,members:a.provenance,onMutated:n})]}),a.summary&&e.jsx(N,{children:e.jsxs(k,{children:[e.jsx("h3",{className:"mb-1 font-heading text-sm font-semibold text-muted-foreground",children:"Summary"}),e.jsx("p",{className:"text-sm leading-relaxed text-foreground/90",children:a.summary})]})}),a.keyMemories.length>0&&e.jsx(N,{children:e.jsxs(k,{children:[e.jsx("h3",{className:"mb-2 font-heading text-sm font-semibold text-muted-foreground",children:"Key memories"}),e.jsx("ul",{className:"flex flex-col gap-2",children:a.keyMemories.map(s=>e.jsxs("li",{className:"rounded-md border border-border/60 bg-bg-elevated/40 p-3 text-sm",children:[e.jsx("p",{className:"line-clamp-3 text-foreground/90",children:s.content}),e.jsxs("p",{className:"mt-1 text-xs text-muted-foreground",children:[s.memoryId," · ",B(s.timestamp)]})]},s.memoryId))})]})}),e.jsx(te,{topicId:t}),a.provenance.length>0&&e.jsx(N,{children:e.jsxs(k,{children:[e.jsx("h3",{className:"mb-2 font-heading text-sm font-semibold text-muted-foreground",children:"Provenance"}),e.jsx("div",{className:"max-h-72 overflow-auto rounded-md border border-border/60",children:e.jsxs("table",{className:"w-full text-sm",children:[e.jsx("thead",{className:"sticky top-0 bg-bg-card",children:e.jsxs("tr",{className:"border-b border-border text-left text-xs uppercase text-muted-foreground",children:[e.jsx("th",{className:"px-3 py-2 font-medium",children:"Memory"}),e.jsx("th",{className:"px-3 py-2 font-medium",children:"Session"}),e.jsx("th",{className:"px-3 py-2 font-medium",children:"Assigned"}),e.jsx("th",{className:"px-3 py-2 font-medium",children:"Method"})]})}),e.jsx("tbody",{children:a.provenance.map(s=>e.jsxs("tr",{className:"border-b border-border/40 last:border-0",children:[e.jsx("td",{className:"px-3 py-1.5 font-mono text-xs",children:s.memoryId}),e.jsx("td",{className:"px-3 py-1.5 text-xs text-muted-foreground",children:s.sessionId||"unknown"}),e.jsx("td",{className:"px-3 py-1.5 text-xs text-muted-foreground",children:B(s.assignedAt)}),e.jsx("td",{className:"px-3 py-1.5",children:e.jsx(y,{variant:"outline",children:re[s.method]??s.method})})]},s.memoryId))})]})})]})}),a.relatedTopicIds.length>0&&e.jsx(N,{children:e.jsxs(k,{children:[e.jsx("h3",{className:"mb-2 font-heading text-sm font-semibold text-muted-foreground",children:"Related topics"}),e.jsx("div",{className:"flex flex-wrap gap-2",children:a.relatedTopicIds.map(s=>e.jsx(y,{variant:"accent",children:u.get(s)??s},s))})]})})]})}const O=900,I=560,F=6e4,W=.012,$=.012,D=.8,S=2,ne=.02,oe=250;function ae(t){return t==="merge"?"#f59e0b":"#22c55e"}function le(t){return t.map((o,m)=>{const i=2*Math.PI*m/Math.max(t.length,1);return{id:o.id,label:o.label,memoryCount:o.memoryCount,pos:{x:Math.cos(i)*220+O/2,y:Math.sin(i)*220+I/2},vx:0,vy:0}})}function ie(t){const{nodes:o,edges:m}=t;for(let i=0;i<o.length;i++){const n=o[i];let a=0,d=0;for(let l=0;l<o.length;l++){if(i===l)continue;const u=n.pos.x-o[l].pos.x,r=n.pos.y-o[l].pos.y,s=u*u+r*r||1,p=Math.sqrt(s);a+=u/p*(F/s),d+=r/p*(F/s)}for(const l of m){let u;if(l.source===i)u=l.target;else if(l.target===i)u=l.source;else continue;const r=o[u].pos.x-n.pos.x,s=o[u].pos.y-n.pos.y;a+=r*W,d+=s*W}a-=n.pos.x*$,d-=n.pos.y*$,n.vx=(n.vx+a)*D,n.vy=(n.vy+d)*D;const h=Math.sqrt(n.vx*n.vx+n.vy*n.vy);h>S&&(n.vx=n.vx/h*S,n.vy=n.vy/h*S),h>ne&&(n.pos.x+=n.vx,n.pos.y+=n.vy)}}function de({data:t}){const o=c.useRef(null),m=c.useRef(0),[,i]=c.useState(0),[n,a]=c.useState(0);c.useEffect(()=>{const r=le(t.nodes),s=new Map(r.map((g,f)=>[g.id,f])),p=t.edges.map(g=>{const f=s.get(g.source),j=s.get(g.target);return f===void 0||j===void 0||f===j?null:{source:f,target:j,kind:g.kind}}).filter(g=>g!==null),b=new Map;for(const g of t.edges)b.set(g.source,Math.max(b.get(g.source)??0,g.at)),b.set(g.target,Math.max(b.get(g.target)??0,g.at));return o.current={nodes:r,edges:p,timeByActivity:b},i(1),()=>cancelAnimationFrame(m.current)},[t]),c.useEffect(()=>{if(!o.current)return;let r=0;const s=()=>{!o.current||r>=oe||(ie(o.current),r++,i(r),m.current=requestAnimationFrame(s))};return m.current=requestAnimationFrame(s),()=>cancelAnimationFrame(m.current)},[t]);const d=o.current,h=Math.max(t.buckets.length-1,0),l=c.useMemo(()=>{if(t.buckets.length===0)return null;const r=Math.min(n,t.buckets.length-1);return t.buckets[r].bucket},[n,t.buckets]);if(!d||t.nodes.length===0)return e.jsx("div",{className:"rounded-lg border border-border bg-bg-card p-6 text-sm text-muted-foreground",children:"No evolution data yet. Merge or split topics to see connections, then check back after a rebuild."});const u=l==null||!d.timeByActivity?null:new Set([...d.timeByActivity.entries()].filter(([,r])=>r<=l).map(([r])=>r));return e.jsxs("div",{className:"flex flex-col gap-3",children:[t.buckets.length>1&&e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx("input",{type:"range",min:0,max:h,value:n,onChange:r=>a(Number(r.target.value)),className:"w-full accent-primary","aria-label":"Scrub through topic activity time"}),e.jsx("span",{className:"whitespace-nowrap text-xs text-muted-foreground",children:l?new Date(l).toLocaleDateString():"all"})]}),e.jsxs("svg",{width:"100%",height:I,viewBox:`0 0 ${O} ${I}`,role:"img","aria-label":"Topic evolution graph",className:"rounded-lg border border-border/60 bg-bg-card",children:[d.edges.map((r,s)=>{const p=d.nodes[r.source],b=d.nodes[r.target];return e.jsx("line",{x1:p.pos.x,y1:p.pos.y,x2:b.pos.x,y2:b.pos.y,stroke:ae(r.kind),strokeWidth:1.5,strokeDasharray:r.kind==="split"?"4 3":void 0,opacity:.5},`edge-${s}`)}),d.nodes.map((r,s)=>{const p=u==null||u.size===0||u.has(r.id),b=Math.min(32,8+r.memoryCount*3);return e.jsxs("g",{opacity:p?1:.15,children:[e.jsx("circle",{cx:r.pos.x,cy:r.pos.y,r:b,fill:"hsl(var(--primary) / 0.18)",stroke:"hsl(var(--primary))",strokeWidth:1.5}),e.jsx("text",{x:r.pos.x,y:r.pos.y+4,textAnchor:"middle",fontSize:"10px",fill:"hsl(var(--foreground))",className:"pointer-events-none",children:r.label.length>16?r.label.slice(0,15)+"…":r.label})]},`node-${s}`)})]}),e.jsxs("div",{className:"flex items-center gap-4 text-xs text-muted-foreground",children:[e.jsxs("span",{className:"flex items-center gap-1.5",children:[e.jsx("span",{className:"inline-block h-0.5 w-6 bg-amber-500"})," merge"]}),e.jsxs("span",{className:"flex items-center gap-1.5",children:[e.jsx("span",{className:"inline-block h-0.5 w-6 border-t border-dashed border-green-500"}),"split"]}),e.jsxs("span",{children:[t.nodes.length," nodes · ",t.edges.length," curation events"]})]})]})}function ce(t){return t.type==="wiki_rebuilt"}function me(){const{data:t,error:o,loading:m,refetch:i}=w(c.useCallback(()=>Q(),[]),{pollInterval:6e4}),{events:n}=X(),a=c.useMemo(()=>{const d=n.filter(ce);return d.length===0?null:d[d.length-1].ts},[n]);return c.useEffect(()=>{a!=null&&i()},[a,i]),o&&!t?e.jsxs("div",{className:"rounded-lg border border-border bg-bg-card p-6 text-sm text-danger",children:["Error loading evolution graph: ",o.message]}):m&&!t?e.jsx("div",{className:"rounded-lg border border-border bg-bg-card p-6 text-sm text-muted-foreground",children:"Loading evolution graph…"}):e.jsx(N,{children:e.jsxs(k,{children:[e.jsx("h3",{className:"mb-2 font-heading text-sm font-semibold text-muted-foreground",children:"Topic evolution"}),t?e.jsx(de,{data:t}):e.jsx("p",{className:"text-sm text-muted-foreground",children:"No data yet."})]})})}const ue={label:"accent",merge:"warning",split:"default"};function _(t){return t?new Date(t).toLocaleString():"—"}function P({view:t,onChange:o}){return e.jsxs("div",{className:"flex gap-1",children:[e.jsx(A,{pressed:t==="list",onClick:()=>o("list"),children:"Topics"}),e.jsx(A,{pressed:t==="evolution",onClick:()=>o("evolution"),children:"Evolution"})]})}function xe({entry:t,onOpen:o}){return e.jsxs("tr",{className:"border-b border-border/60 last:border-0 hover:bg-bg-elevated/40",children:[e.jsx("td",{className:"py-2 pr-3",children:e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("span",{className:"font-medium text-foreground",children:t.label}),t.edited&&e.jsx(y,{variant:"outline",children:"edited"})]})}),e.jsx("td",{className:"py-2 pr-3 text-sm text-muted-foreground",children:t.memoryCount}),e.jsx("td",{className:"py-2 pr-3",children:e.jsx("div",{className:"flex flex-wrap gap-1",children:t.overrideKinds.map(m=>e.jsx(y,{variant:ue[m]??"default",children:m},m))})}),e.jsx("td",{className:"py-2 pr-3 text-xs text-muted-foreground",children:_(t.lastUpdated)}),e.jsx("td",{className:"py-2 text-right",children:e.jsx(v,{variant:"outline",size:"sm",onClick:()=>o(t.id),children:"Open"})})]})}function je(){const{data:t,loading:o,error:m}=w(c.useCallback(()=>z(),[]),{pollInterval:3e4}),[i,n]=c.useState("list"),[a,d]=c.useState(""),[h,l]=c.useState(null),u=c.useMemo(()=>{if(!t)return[];const r=a.trim().toLowerCase();return r?t.topics.filter(s=>s.label.toLowerCase().includes(r)):t.topics},[t,a]);return h?e.jsx(se,{topicId:h,onBack:()=>l(null)}):i==="evolution"?e.jsxs("div",{className:"flex flex-col gap-3",children:[e.jsx(P,{view:i,onChange:n}),e.jsx(me,{})]}):m&&!t?e.jsxs("div",{className:"rounded-lg border border-border bg-bg-card p-6 text-sm text-danger",children:["Error loading wiki: ",m.message]}):o&&!t?e.jsx("div",{className:"rounded-lg border border-border bg-bg-card p-6 text-sm text-muted-foreground",children:"Loading wiki…"}):t?t.totalTopics===0?e.jsxs("div",{className:"rounded-lg border border-border bg-bg-card p-6 text-sm text-muted-foreground",children:[e.jsx("h3",{className:"mb-2 font-heading text-base text-foreground",children:"Wiki"}),e.jsx("p",{children:"No topics yet. Topics are auto-generated after every Nth compaction from real memory embeddings (k-means + TF-IDF)."}),e.jsx("p",{className:"mt-2",children:"Check back after a few more compaction cycles."})]}):e.jsxs("div",{className:"flex flex-col gap-4",children:[e.jsxs("div",{className:"flex flex-wrap items-end justify-between gap-2",children:[e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx(P,{view:i,onChange:n}),e.jsxs("div",{children:[e.jsx("h2",{className:"font-heading text-lg font-semibold",children:"Wiki"}),e.jsxs("p",{className:"text-sm text-muted-foreground",children:[t.totalTopics," topic",t.totalTopics!==1?"s":""," ·"," ",t.totalMemories," assigned memori",t.totalMemories!==1?"es":"y",t.lastRebuildAt!=null&&e.jsxs(e.Fragment,{children:[" · last rebuild ",_(t.lastRebuildAt)]})]})]})]}),e.jsx("input",{type:"search",placeholder:"Filter topics by label…","aria-label":"Filter topics by label",value:a,onChange:r=>d(r.target.value),className:"w-64 rounded-md border border-border bg-bg-elevated/50 px-3 py-2 text-sm outline-none transition-colors focus:border-primary"})]}),e.jsx("div",{className:"overflow-x-auto rounded-lg border border-border bg-bg-card",children:e.jsxs("table",{className:"w-full table-fixed text-sm",children:[e.jsx("thead",{children:e.jsxs("tr",{className:"border-b border-border text-left text-xs uppercase text-muted-foreground",children:[e.jsx("th",{className:"w-1/3 px-4 py-2 font-medium",children:"Label"}),e.jsx("th",{className:"w-16 py-2 font-medium",children:"Memories"}),e.jsx("th",{className:"w-40 py-2 font-medium",children:"Badges"}),e.jsx("th",{className:"w-40 py-2 font-medium",children:"Last updated"}),e.jsx("th",{className:"w-20 py-2"})]})}),e.jsxs("tbody",{children:[u.map(r=>e.jsx(xe,{entry:r,onOpen:l},r.id)),u.length===0&&e.jsx("tr",{children:e.jsxs("td",{colSpan:5,className:"px-4 py-6 text-center text-muted-foreground",children:['No topics match "',a,'".']})})]})]})})]}):e.jsx("div",{className:"rounded-lg border border-border bg-bg-card p-6 text-sm text-muted-foreground",children:"No wiki data available."})}export{je as default};
|
|
2
|
+
//# sourceMappingURL=WikiTab-1LcQrXk3.js.map
|
package/extensions/dashboard-client/dist/assets/{WikiTab-99sKE4nB.js.map → WikiTab-1LcQrXk3.js.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WikiTab-99sKE4nB.js","sources":["../../src/tabs/WikiTab/WikiPageControls.tsx","../../src/tabs/WikiTab/TopicTimeline.tsx","../../src/tabs/WikiTab/WikiPage.tsx","../../src/tabs/WikiTab/TopicEvolutionGraph.tsx","../../src/tabs/WikiTab/TopicEvolutionView.tsx","../../src/tabs/WikiTab.tsx"],"sourcesContent":["/**\n * dashboard-client/src/tabs/WikiTab/WikiPageControls.tsx — rename/merge/split.\n *\n * Controlled dialogs for the three curation mutations. Non-destructive: on\n * success the parent refetches and paints the returned CurationResult; on\n * failure the pre-mutation snapshot stays (no local partial apply).\n *\n * Styling: Tailwind + shadcn (Button). No legacy CSS classes.\n */\n\nimport type React from \"react\";\nimport { useState } from \"react\";\nimport { renameTopic, mergeTopics, splitTopic } from \"../../api/client\";\nimport type { MemoryProvenance } from \"@contracts\";\nimport { Button } from \"../../components/ui/button\";\n\ninterface ControlsProps {\n\ttopicId: string;\n\tothers: Array<{ id: string; label: string }>;\n\tmembers: MemoryProvenance[];\n\tonMutated: () => void;\n}\n\ntype Mode = \"rename\" | \"merge\" | \"split\" | null;\n\nfunction memberSnippet(id: string, method: string): string {\n\tconst base = id.length > 14 ? id.slice(0, 14) + \"…\" : id;\n\treturn method ? `${base} (${method})` : base;\n}\n\nexport default function WikiPageControls({\n\ttopicId,\n\tothers,\n\tmembers,\n\tonMutated,\n}: ControlsProps): React.ReactElement {\n\tconst [mode, setMode] = useState<Mode>(null);\n\tconst [label, setLabel] = useState(\"\");\n\tconst [target, setTarget] = useState(\"\");\n\tconst [picked, setPicked] = useState<string[]>([]);\n\tconst [busy, setBusy] = useState(false);\n\tconst [error, setError] = useState<string | null>(null);\n\n\tfunction open(m: Exclude<Mode, null>): void {\n\t\tsetMode(m);\n\t\tsetError(null);\n\t\tsetLabel(\"\");\n\t\tsetTarget(\"\");\n\t\tsetPicked([]);\n\t}\n\n\tasync function submit(): Promise<void> {\n\t\tsetBusy(true);\n\t\tsetError(null);\n\t\ttry {\n\t\t\tif (mode === \"rename\") {\n\t\t\t\tawait renameTopic(topicId, label);\n\t\t\t} else if (mode === \"merge\") {\n\t\t\t\tif (!target) {\n\t\t\t\t\tsetError(\"Pick a target topic.\");\n\t\t\t\t\tsetBusy(false);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tawait mergeTopics(topicId, target);\n\t\t\t} else if (mode === \"split\") {\n\t\t\t\tif (picked.length === 0) {\n\t\t\t\t\tsetError(\"Pick at least one memory.\");\n\t\t\t\t\tsetBusy(false);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tawait splitTopic(topicId, picked);\n\t\t\t}\n\t\t\tsetMode(null);\n\t\t\tonMutated();\n\t\t} catch (e) {\n\t\t\tsetError(e instanceof Error ? e.message : String(e));\n\t\t} finally {\n\t\t\tsetBusy(false);\n\t\t}\n\t}\n\n\treturn (\n\t\t<>\n\t\t\t<div className=\"flex gap-2\">\n\t\t\t\t<Button variant=\"outline\" size=\"sm\" onClick={() => open(\"rename\")}>\n\t\t\t\t\tRename\n\t\t\t\t</Button>\n\t\t\t\t<Button variant=\"outline\" size=\"sm\" onClick={() => open(\"merge\")}>\n\t\t\t\t\tMerge into…\n\t\t\t\t</Button>\n\t\t\t\t<Button variant=\"outline\" size=\"sm\" onClick={() => open(\"split\")}>\n\t\t\t\t\tSplit\n\t\t\t\t</Button>\n\t\t\t</div>\n\n\t\t\t{mode && (\n\t\t\t\t<div\n\t\t\t\t\tclassName=\"fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4\"\n\t\t\t\t\tonClick={() => setMode(null)}\n\t\t\t\t>\n\t\t\t\t\t<div\n\t\t\t\t\t\tclassName=\"w-full max-w-md rounded-lg border border-border bg-bg-card p-5 shadow-xl\"\n\t\t\t\t\t\trole=\"dialog\"\n\t\t\t\t\t\taria-modal=\"true\"\n\t\t\t\t\t\taria-label={mode}\n\t\t\t\t\t\tonClick={(e) => e.stopPropagation()}\n\t\t\t\t\t>\n\t\t\t\t\t\t<h3 className=\"mb-3 font-heading text-base font-semibold text-foreground\">\n\t\t\t\t\t\t\t{mode === \"rename\"\n\t\t\t\t\t\t\t\t? \"Rename topic\"\n\t\t\t\t\t\t\t\t: mode === \"merge\"\n\t\t\t\t\t\t\t\t\t? \"Merge into…\"\n\t\t\t\t\t\t\t\t\t: \"Split topic\"}\n\t\t\t\t\t\t</h3>\n\n\t\t\t\t\t\t{mode === \"rename\" && (\n\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\ttype=\"text\"\n\t\t\t\t\t\t\t\tvalue={label}\n\t\t\t\t\t\t\t\tplaceholder=\"New label (empty = back to auto)\"\n\t\t\t\t\t\t\t\taria-label=\"New topic label\"\n\t\t\t\t\t\t\t\tautoFocus\n\t\t\t\t\t\t\t\tonChange={(e) => setLabel(e.target.value)}\n\t\t\t\t\t\t\t\tclassName=\"w-full rounded-md border border-border bg-bg-elevated/50 px-3 py-2 text-sm outline-none transition-colors focus:border-primary\"\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t{mode === \"merge\" && (\n\t\t\t\t\t\t\t<select\n\t\t\t\t\t\t\t\tvalue={target}\n\t\t\t\t\t\t\t\taria-label=\"Target topic\"\n\t\t\t\t\t\t\t\tonChange={(e) => setTarget(e.target.value)}\n\t\t\t\t\t\t\t\tclassName=\"w-full rounded-md border border-border bg-bg-elevated/50 px-3 py-2 text-sm outline-none transition-colors focus:border-primary\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<option value=\"\">Pick a target topic…</option>\n\t\t\t\t\t\t\t\t{others.map((o) => (\n\t\t\t\t\t\t\t\t\t<option key={o.id} value={o.id}>\n\t\t\t\t\t\t\t\t\t\t{o.label}\n\t\t\t\t\t\t\t\t\t</option>\n\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t</select>\n\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t{mode === \"split\" && (\n\t\t\t\t\t\t\t<div className=\"max-h-56 overflow-auto rounded-md border border-border/60\">\n\t\t\t\t\t\t\t\t{members.map((m) => (\n\t\t\t\t\t\t\t\t\t<label\n\t\t\t\t\t\t\t\t\t\tkey={m.memoryId}\n\t\t\t\t\t\t\t\t\t\tclassName=\"flex cursor-pointer items-center gap-2 border-b border-border/40 px-3 py-2 text-sm last:border-0 hover:bg-bg-elevated/40\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\t\t\t\t\t\tchecked={picked.includes(m.memoryId)}\n\t\t\t\t\t\t\t\t\t\t\tonChange={(e) => {\n\t\t\t\t\t\t\t\t\t\t\t\tsetPicked((prev) =>\n\t\t\t\t\t\t\t\t\t\t\t\t\te.target.checked\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t? [...prev, m.memoryId]\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t: prev.filter((x) => x !== m.memoryId),\n\t\t\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\tclassName=\"accent-primary\"\n\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t<span className=\"font-mono text-xs text-muted-foreground\">\n\t\t\t\t\t\t\t\t\t\t\t{memberSnippet(m.memoryId, m.method)}\n\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t{error && (\n\t\t\t\t\t\t\t<p className=\"mt-3 text-sm text-danger\">{error}</p>\n\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t<div className=\"mt-4 flex justify-end gap-2\">\n\t\t\t\t\t\t\t<Button variant=\"ghost\" size=\"sm\" onClick={() => setMode(null)}>\n\t\t\t\t\t\t\t\tCancel\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t<Button size=\"sm\" onClick={submit} disabled={busy}>\n\t\t\t\t\t\t\t\t{busy ? \"…\" : \"Confirm\"}\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</>\n\t);\n}\n","/**\n * dashboard-client/src/tabs/WikiTab/TopicTimeline.tsx — per-topic timeline.\n *\n * Renders topic memory-addition buckets as a lightweight SVG bar strip (no\n * chart library needed). X = time bucket, bar height = memories added.\n * Fetches GET /api/wiki/topic/:topicId/timeline.\n *\n * Styling: Tailwind + shadcn (Card). No legacy CSS classes.\n */\n\nimport type React from \"react\";\nimport { useCallback } from \"react\";\nimport { useApi } from \"../../hooks/useApi\";\nimport { fetchTopicTimeline } from \"../../api/client\";\nimport type { TopicTimelineResponse } from \"@contracts\";\n\nconst W = 720;\nconst H = 56;\nconst BAR_W = 10;\nconst GAP = 3;\n\ninterface Props {\n\ttopicId: string;\n}\n\nfunction fmtBucket(ts: number): string {\n\tif (!ts) return \"—\";\n\treturn new Date(ts).toLocaleDateString();\n}\n\nexport default function TopicTimeline({ topicId }: Props): React.ReactElement {\n\tconst { data, error } = useApi<TopicTimelineResponse>(\n\t\tuseCallback(() => fetchTopicTimeline(topicId), [topicId]),\n\t\t{},\n\t);\n\n\tif (error) {\n\t\treturn (\n\t\t\t<div className=\"rounded-lg border border-border bg-bg-card p-4 text-sm text-muted-foreground\">\n\t\t\t\tTimeline unavailable.\n\t\t\t</div>\n\t\t);\n\t}\n\tif (!data) {\n\t\treturn (\n\t\t\t<div className=\"rounded-lg border border-border bg-bg-card p-4 text-sm text-muted-foreground\">\n\t\t\t\tLoading timeline…\n\t\t\t</div>\n\t\t);\n\t}\n\n\tconst { buckets, total } = data;\n\tconst max = Math.max(...buckets.map((b) => b.count), 1);\n\tconst totalWidth = buckets.length * (BAR_W + GAP);\n\tconst scale = Math.min(1, (W - 24) / Math.max(totalWidth, 1));\n\n\treturn (\n\t\t<div className=\"rounded-lg border border-border bg-bg-card p-4\">\n\t\t\t<h3 className=\"mb-2 font-heading text-sm font-semibold text-muted-foreground\">\n\t\t\t\tMemory timeline{\" \"}\n\t\t\t\t<span className=\"font-normal text-xs\">\n\t\t\t\t\t({total} memory{total !== 1 ? \"ies\" : \"y\"} · daily buckets)\n\t\t\t\t</span>\n\t\t\t</h3>\n\t\t\t{buckets.length === 0 ? (\n\t\t\t\t<p className=\"text-sm text-muted-foreground\">No timeline data yet.</p>\n\t\t\t) : (\n\t\t\t\t<>\n\t\t\t\t\t<svg\n\t\t\t\t\t\twidth=\"100%\"\n\t\t\t\t\t\theight={H}\n\t\t\t\t\t\tviewBox={`0 0 ${Math.max(W, totalWidth * scale)} ${H}`}\n\t\t\t\t\t\trole=\"img\"\n\t\t\t\t\t\taria-label=\"Memory additions over time\"\n\t\t\t\t\t>\n\t\t\t\t\t\t{buckets.map((b, i) => {\n\t\t\t\t\t\t\tconst x = i * (BAR_W + GAP) * scale;\n\t\t\t\t\t\t\tconst h = Math.max(3, (b.count / max) * (H - 20));\n\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t<rect\n\t\t\t\t\t\t\t\t\tkey={`${b.bucket}-${i}`}\n\t\t\t\t\t\t\t\t\tx={x}\n\t\t\t\t\t\t\t\t\ty={H - 6 - h}\n\t\t\t\t\t\t\t\t\twidth={Math.max(2, BAR_W * scale)}\n\t\t\t\t\t\t\t\t\theight={h}\n\t\t\t\t\t\t\t\t\trx={2}\n\t\t\t\t\t\t\t\t\tfill=\"hsl(var(--primary))\"\n\t\t\t\t\t\t\t\t\topacity={0.85}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<title>{`${fmtBucket(b.bucket)}: ${b.count}`}</title>\n\t\t\t\t\t\t\t\t</rect>\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t})}\n\t\t\t\t\t</svg>\n\t\t\t\t\t<p className=\"mt-1 text-xs text-muted-foreground\">\n\t\t\t\t\t\tFirst display: {fmtBucket(buckets[0].bucket)} · last:\n\t\t\t\t\t\t{fmtBucket(buckets[buckets.length - 1].bucket)}\n\t\t\t\t\t</p>\n\t\t\t\t</>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n","/**\n * dashboard-client/src/tabs/WikiTab/WikiPage.tsx — single wiki topic page.\n *\n * Renders the extractive summary, key member memories, the member provenance\n * table (memory → session/assigned-at/method), related topics (resolved from\n * the wiki index), and the per-topic memory timeline. Mutations\n * (rename/merge/split) delegate to WikiPageControls; on mutation success the\n * page + index are refetched.\n *\n * Styling: Tailwind + shadcn (Card, Badge, Button) — no legacy CSS.\n */\n\nimport type React from \"react\";\nimport { useCallback, useMemo, useState } from \"react\";\nimport { useApi } from \"../../hooks/useApi\";\nimport { fetchWikiTopic, fetchWikiIndex } from \"../../api/client\";\nimport type { WikiPageResponse, WikiIndexResponse } from \"@contracts\";\nimport { Badge } from \"../../components/ui/badge\";\nimport { Button } from \"../../components/ui/button\";\nimport { Card, CardContent } from \"../../components/ui/card\";\nimport WikiPageControls from \"./WikiPageControls\";\nimport TopicTimeline from \"./TopicTimeline\";\n\ninterface WikiPageProps {\n\ttopicId: string;\n\tonBack: () => void;\n}\n\nfunction fmtTs(ms: number): string {\n\tif (!ms) return \"—\";\n\treturn new Date(ms).toLocaleString();\n}\n\nconst METHOD_LABELS: Record<string, string> = {\n\t\"kmeans+tfidf\": \"kmeans\",\n\tmerge: \"merge\",\n\tsplit: \"split\",\n\tmanual: \"manual\",\n};\n\nexport default function WikiPage({\n\ttopicId,\n\tonBack,\n}: WikiPageProps): React.ReactElement {\n\tconst [dirty, setDirty] = useState(0);\n\tconst bump = useCallback(() => setDirty((d) => d + 1), []);\n\n\tconst { data: page, error, loading } = useApi<WikiPageResponse>(\n\t\tuseCallback(() => fetchWikiTopic(topicId), [topicId, dirty]),\n\t\t{},\n\t);\n\tconst { data: index } = useApi<WikiIndexResponse>(\n\t\tuseCallback(() => fetchWikiIndex(), []),\n\t\t{},\n\t);\n\n\tconst labelOf = useMemo(() => {\n\t\tconst map = new Map<string, string>();\n\t\tfor (const t of index?.topics ?? []) map.set(t.id, t.label);\n\t\treturn map;\n\t}, [index]);\n\n\tif (error && !page) {\n\t\treturn (\n\t\t\t<div className=\"rounded-lg border border-border bg-bg-card p-6 text-sm text-danger\">\n\t\t\t\tError loading topic: {error.message}\n\t\t\t</div>\n\t\t);\n\t}\n\tif (loading && !page) {\n\t\treturn (\n\t\t\t<div className=\"rounded-lg border border-border bg-bg-card p-6 text-sm text-muted-foreground\">\n\t\t\t\tLoading topic…\n\t\t\t</div>\n\t\t);\n\t}\n\tif (!page) {\n\t\treturn (\n\t\t\t<div className=\"rounded-lg border border-border bg-bg-card p-6 text-sm text-muted-foreground\">\n\t\t\t\tNo topic data available.\n\t\t\t</div>\n\t\t);\n\t}\n\n\tconst otherTopics = (index?.topics ?? [])\n\t\t.filter((t) => t.id !== topicId)\n\t\t.map((t) => ({ id: t.id, label: t.label }));\n\n\treturn (\n\t\t<div className=\"flex flex-col gap-4\">\n\t\t\t<div className=\"flex items-center justify-between\">\n\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t<Button variant=\"outline\" size=\"sm\" onClick={onBack}>\n\t\t\t\t\t\t← Wiki\n\t\t\t\t\t</Button>\n\t\t\t\t\t<h2 className=\"font-heading text-lg font-semibold\">\n\t\t\t\t\t\t{page.topic.label}\n\t\t\t\t\t</h2>\n\t\t\t\t\t{page.topic.edited && <Badge variant=\"outline\">edited</Badge>}\n\t\t\t\t\t<Badge variant=\"default\">\n\t\t\t\t\t\t{page.provenance.length} memories\n\t\t\t\t\t</Badge>\n\t\t\t\t</div>\n\t\t\t\t<WikiPageControls\n\t\t\t\t\ttopicId={topicId}\n\t\t\t\t\tothers={otherTopics}\n\t\t\t\t\tmembers={page.provenance}\n\t\t\t\t\tonMutated={bump}\n\t\t\t\t/>\n\t\t\t</div>\n\n\t\t\t{page.summary && (\n\t\t\t\t<Card>\n\t\t\t\t\t<CardContent>\n\t\t\t\t\t\t<h3 className=\"mb-1 font-heading text-sm font-semibold text-muted-foreground\">\n\t\t\t\t\t\t\tSummary\n\t\t\t\t\t\t</h3>\n\t\t\t\t\t\t<p className=\"text-sm leading-relaxed text-foreground/90\">\n\t\t\t\t\t\t\t{page.summary}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t</CardContent>\n\t\t\t\t</Card>\n\t\t\t)}\n\n\t\t\t{page.keyMemories.length > 0 && (\n\t\t\t\t<Card>\n\t\t\t\t\t<CardContent>\n\t\t\t\t\t\t<h3 className=\"mb-2 font-heading text-sm font-semibold text-muted-foreground\">\n\t\t\t\t\t\t\tKey memories\n\t\t\t\t\t\t</h3>\n\t\t\t\t\t\t<ul className=\"flex flex-col gap-2\">\n\t\t\t\t\t\t\t{page.keyMemories.map((m) => (\n\t\t\t\t\t\t\t\t<li\n\t\t\t\t\t\t\t\t\tkey={m.memoryId}\n\t\t\t\t\t\t\t\t\tclassName=\"rounded-md border border-border/60 bg-bg-elevated/40 p-3 text-sm\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<p className=\"line-clamp-3 text-foreground/90\">{m.content}</p>\n\t\t\t\t\t\t\t\t\t<p className=\"mt-1 text-xs text-muted-foreground\">\n\t\t\t\t\t\t\t\t\t\t{m.memoryId} · {fmtTs(m.timestamp)}\n\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</ul>\n\t\t\t\t\t</CardContent>\n\t\t\t\t</Card>\n\t\t\t)}\n\n\t\t\t<TopicTimeline topicId={topicId} />\n\n\t\t\t{page.provenance.length > 0 && (\n\t\t\t\t<Card>\n\t\t\t\t\t<CardContent>\n\t\t\t\t\t\t<h3 className=\"mb-2 font-heading text-sm font-semibold text-muted-foreground\">\n\t\t\t\t\t\t\tProvenance\n\t\t\t\t\t\t</h3>\n\t\t\t\t\t\t<div className=\"max-h-72 overflow-auto rounded-md border border-border/60\">\n\t\t\t\t\t\t\t<table className=\"w-full text-sm\">\n\t\t\t\t\t\t\t\t<thead className=\"sticky top-0 bg-bg-card\">\n\t\t\t\t\t\t\t\t\t<tr className=\"border-b border-border text-left text-xs uppercase text-muted-foreground\">\n\t\t\t\t\t\t\t\t\t\t<th className=\"px-3 py-2 font-medium\">Memory</th>\n\t\t\t\t\t\t\t\t\t\t<th className=\"px-3 py-2 font-medium\">Session</th>\n\t\t\t\t\t\t\t\t\t\t<th className=\"px-3 py-2 font-medium\">Assigned</th>\n\t\t\t\t\t\t\t\t\t\t<th className=\"px-3 py-2 font-medium\">Method</th>\n\t\t\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t\t</thead>\n\t\t\t\t\t\t\t\t<tbody>\n\t\t\t\t\t\t\t\t\t{page.provenance.map((p) => (\n\t\t\t\t\t\t\t\t\t\t<tr\n\t\t\t\t\t\t\t\t\t\t\tkey={p.memoryId}\n\t\t\t\t\t\t\t\t\t\t\tclassName=\"border-b border-border/40 last:border-0\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t<td className=\"px-3 py-1.5 font-mono text-xs\">\n\t\t\t\t\t\t\t\t\t\t\t\t{p.memoryId}\n\t\t\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t\t\t<td className=\"px-3 py-1.5 text-xs text-muted-foreground\">\n\t\t\t\t\t\t\t\t\t\t\t\t{p.sessionId || \"unknown\"}\n\t\t\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t\t\t<td className=\"px-3 py-1.5 text-xs text-muted-foreground\">\n\t\t\t\t\t\t\t\t\t\t\t\t{fmtTs(p.assignedAt)}\n\t\t\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t\t\t<td className=\"px-3 py-1.5\">\n\t\t\t\t\t\t\t\t\t\t\t\t<Badge variant=\"outline\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t{METHOD_LABELS[p.method] ?? p.method}\n\t\t\t\t\t\t\t\t\t\t\t\t</Badge>\n\t\t\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t\t</tbody>\n\t\t\t\t\t\t\t</table>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</CardContent>\n\t\t\t\t</Card>\n\t\t\t)}\n\n\t\t\t{page.relatedTopicIds.length > 0 && (\n\t\t\t\t<Card>\n\t\t\t\t\t<CardContent>\n\t\t\t\t\t\t<h3 className=\"mb-2 font-heading text-sm font-semibold text-muted-foreground\">\n\t\t\t\t\t\t\tRelated topics\n\t\t\t\t\t\t</h3>\n\t\t\t\t\t\t<div className=\"flex flex-wrap gap-2\">\n\t\t\t\t\t\t\t{page.relatedTopicIds.map((id) => (\n\t\t\t\t\t\t\t\t<Badge key={id} variant=\"accent\">\n\t\t\t\t\t\t\t\t\t{labelOf.get(id) ?? id}\n\t\t\t\t\t\t\t\t</Badge>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</CardContent>\n\t\t\t\t</Card>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n","/**\n * dashboard-client/src/tabs/WikiTab/TopicEvolutionGraph.tsx — D3 evolution.\n *\n * Force-directed graph of wiki topics over time. Nodes are topics (radius scales\n * with memoryCount); edges are merge/split curation events. A bucket scrubber\n * (from the response buckets) dims nodes that had no activity up to the selected\n * time window — a lightweight \"growth over time\" view.\n *\n * Self-contained force simulation (Coulomb repulsion + Hooke attraction +\n * center gravity) — the codebase deliberately avoids external graph libraries\n * (see memory-map-layout.ts). No data is fabricated: only real node/edge/bucket\n * fields from the contract are used. Styling is Tailwind.\n */\n\nimport type React from \"react\";\nimport { useEffect, useMemo, useRef, useState } from \"react\";\nimport type { TopicEvolutionResponse } from \"@contracts\";\n\n// ── Simulation constants ────────────────────────────────────────────────\nconst W = 900;\nconst H = 560;\nconst REPULSION = 60_000;\nconst ATTRACTION = 0.012;\nconst CENTER_GRAVITY = 0.012;\nconst DAMPING = 0.8;\nconst SPEED_LIMIT = 2;\nconst MIN_VELOCITY = 0.02;\nconst MAX_ITERATIONS = 250;\n\ninterface SimNode {\n\tid: string;\n\tlabel: string;\n\tmemoryCount: number;\n\tpos: { x: number; y: number };\n\tvx: number;\n\tvy: number;\n}\n\ninterface SimEdge {\n\tsource: number;\n\ttarget: number;\n\tkind: \"merge\" | \"split\";\n}\n\nfunction edgeColor(kind: \"merge\" | \"split\"): string {\n\treturn kind === \"merge\" ? \"#f59e0b\" : \"#22c55e\"; // amber / green\n}\n\nfunction initialLayout(nodes: TopicEvolutionResponse[\"nodes\"]): SimNode[] {\n\treturn nodes.map((n, i) => {\n\t\tconst angle = (2 * Math.PI * i) / Math.max(nodes.length, 1);\n\t\treturn {\n\t\t\tid: n.id,\n\t\t\tlabel: n.label,\n\t\t\tmemoryCount: n.memoryCount,\n\t\t\tpos: { x: Math.cos(angle) * 220 + W / 2, y: Math.sin(angle) * 220 + H / 2 },\n\t\t\tvx: 0,\n\t\t\tvy: 0,\n\t\t};\n\t});\n}\n\nfunction applyForces(sim: { nodes: SimNode[]; edges: SimEdge[] }): void {\n\tconst { nodes, edges } = sim;\n\tfor (let i = 0; i < nodes.length; i++) {\n\t\tconst n = nodes[i];\n\t\tlet fx = 0;\n\t\tlet fy = 0;\n\t\tfor (let j = 0; j < nodes.length; j++) {\n\t\t\tif (i === j) continue;\n\t\t\tconst dx = n.pos.x - nodes[j].pos.x;\n\t\t\tconst dy = n.pos.y - nodes[j].pos.y;\n\t\t\tconst distSq = dx * dx + dy * dy || 1;\n\t\t\tconst dist = Math.sqrt(distSq);\n\t\t\tfx += (dx / dist) * (REPULSION / distSq);\n\t\t\tfy += (dy / dist) * (REPULSION / distSq);\n\t\t}\n\t\tfor (const e of edges) {\n\t\t\tlet other: number;\n\t\t\tif (e.source === i) other = e.target;\n\t\t\telse if (e.target === i) other = e.source;\n\t\t\telse continue;\n\t\t\tconst dx = nodes[other].pos.x - n.pos.x;\n\t\t\tconst dy = nodes[other].pos.y - n.pos.y;\n\t\t\tfx += dx * ATTRACTION;\n\t\t\tfy += dy * ATTRACTION;\n\t\t}\n\t\tfx -= n.pos.x * CENTER_GRAVITY;\n\t\tfy -= n.pos.y * CENTER_GRAVITY;\n\t\tn.vx = (n.vx + fx) * DAMPING;\n\t\tn.vy = (n.vy + fy) * DAMPING;\n\t\tconst speed = Math.sqrt(n.vx * n.vx + n.vy * n.vy);\n\t\tif (speed > SPEED_LIMIT) {\n\t\t\tn.vx = (n.vx / speed) * SPEED_LIMIT;\n\t\t\tn.vy = (n.vy / speed) * SPEED_LIMIT;\n\t\t}\n\t\tif (speed > MIN_VELOCITY) {\n\t\t\tn.pos.x += n.vx;\n\t\t\tn.pos.y += n.vy;\n\t\t}\n\t}\n}\n\ninterface Props {\n\tdata: TopicEvolutionResponse;\n}\n\nexport default function TopicEvolutionGraph({\n\tdata,\n}: Props): React.ReactElement {\n\tconst simRef = useRef<{\n\t\tnodes: SimNode[];\n\t\tedges: SimEdge[];\n\t\ttimeByActivity: Map<string, number> | null;\n\t} | null>(null);\n\tconst animRef = useRef(0);\n\tconst [, setFrame] = useState(0);\n\tconst [scrub, setScrub] = useState<number>(0);\n\n\t// Initialize simulation from data (re-run when data changes).\n\tuseEffect(() => {\n\t\tconst nodes = initialLayout(data.nodes);\n\t\tconst indexById = new Map(nodes.map((n, i) => [n.id, i]));\n\t\tconst edges: SimEdge[] = data.edges\n\t\t\t.map((e) => {\n\t\t\t\tconst si = indexById.get(e.source);\n\t\t\t\tconst ti = indexById.get(e.target);\n\t\t\t\tif (si === undefined || ti === undefined || si === ti) return null;\n\t\t\t\treturn { source: si, target: ti, kind: e.kind };\n\t\t\t})\n\t\t\t.filter((e): e is SimEdge => e !== null);\n\t\t// Latest activity per node (from merge/split edge timestamps).\n\t\tconst timeByActivity = new Map<string, number>();\n\t\tfor (const e of data.edges) {\n\t\t\ttimeByActivity.set(e.source, Math.max(timeByActivity.get(e.source) ?? 0, e.at));\n\t\t\ttimeByActivity.set(e.target, Math.max(timeByActivity.get(e.target) ?? 0, e.at));\n\t\t}\n\t\tsimRef.current = { nodes, edges, timeByActivity };\n\t\tsetFrame(1);\n\t\treturn () => cancelAnimationFrame(animRef.current);\n\t}, [data]);\n\n\t// Run the force simulation to stable layout.\n\tuseEffect(() => {\n\t\tif (!simRef.current) return;\n\t\tlet iter = 0;\n\t\tconst tick = () => {\n\t\t\tif (!simRef.current || iter >= MAX_ITERATIONS) return;\n\t\t\tapplyForces(simRef.current);\n\t\t\titer++;\n\t\t\tsetFrame(iter);\n\t\t\tanimRef.current = requestAnimationFrame(tick);\n\t\t};\n\t\tanimRef.current = requestAnimationFrame(tick);\n\t\treturn () => cancelAnimationFrame(animRef.current);\n\t}, [data]);\n\n\tconst sim = simRef.current;\n\n\tconst scrubMax = Math.max(data.buckets.length - 1, 0);\n\tconst scrubBucket = useMemo(() => {\n\t\tif (data.buckets.length === 0) return null;\n\t\tconst idx = Math.min(scrub, data.buckets.length - 1);\n\t\treturn data.buckets[idx].bucket;\n\t}, [scrub, data.buckets]);\n\n\tif (!sim || data.nodes.length === 0) {\n\t\treturn (\n\t\t\t<div className=\"rounded-lg border border-border bg-bg-card p-6 text-sm text-muted-foreground\">\n\t\t\t\tNo evolution data yet. Merge or split topics to see connections, then\n\t\t\t\tcheck back after a rebuild.\n\t\t\t</div>\n\t\t);\n\t}\n\n\tconst activeSet =\n\t\tscrubBucket == null || !sim.timeByActivity\n\t\t\t? null\n\t\t\t: new Set(\n\t\t\t\t\t[...sim.timeByActivity.entries()]\n\t\t\t\t\t\t.filter(([, t]) => t <= scrubBucket)\n\t\t\t\t\t\t.map(([id]) => id),\n\t\t\t\t);\n\n\treturn (\n\t\t<div className=\"flex flex-col gap-3\">\n\t\t\t{/* Scrubber */}\n\t\t\t{data.buckets.length > 1 && (\n\t\t\t\t<div className=\"flex items-center gap-3\">\n\t\t\t\t\t<input\n\t\t\t\t\t\ttype=\"range\"\n\t\t\t\t\t\tmin={0}\n\t\t\t\t\t\tmax={scrubMax}\n\t\t\t\t\t\tvalue={scrub}\n\t\t\t\t\t\tonChange={(e) => setScrub(Number(e.target.value))}\n\t\t\t\t\t\tclassName=\"w-full accent-primary\"\n\t\t\t\t\t\taria-label=\"Scrub through topic activity time\"\n\t\t\t\t\t/>\n\t\t\t\t\t<span className=\"whitespace-nowrap text-xs text-muted-foreground\">\n\t\t\t\t\t\t{scrubBucket ? new Date(scrubBucket).toLocaleDateString() : \"all\"}\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{/* SVG graph */}\n\t\t\t<svg\n\t\t\t\twidth=\"100%\"\n\t\t\t\theight={H}\n\t\t\t\tviewBox={`0 0 ${W} ${H}`}\n\t\t\t\trole=\"img\"\n\t\t\t\taria-label=\"Topic evolution graph\"\n\t\t\t\tclassName=\"rounded-lg border border-border/60 bg-bg-card\"\n\t\t\t>\n\t\t\t\t{sim.edges.map((e, i) => {\n\t\t\t\t\tconst a = sim.nodes[e.source];\n\t\t\t\t\tconst b = sim.nodes[e.target];\n\t\t\t\t\treturn (\n\t\t\t\t\t\t<line\n\t\t\t\t\t\t\tkey={`edge-${i}`}\n\t\t\t\t\t\t\tx1={a.pos.x}\n\t\t\t\t\t\t\ty1={a.pos.y}\n\t\t\t\t\t\t\tx2={b.pos.x}\n\t\t\t\t\t\t\ty2={b.pos.y}\n\t\t\t\t\t\t\tstroke={edgeColor(e.kind)}\n\t\t\t\t\t\t\tstrokeWidth={1.5}\n\t\t\t\t\t\t\tstrokeDasharray={e.kind === \"split\" ? \"4 3\" : undefined}\n\t\t\t\t\t\t\topacity={0.5}\n\t\t\t\t\t\t/>\n\t\t\t\t\t);\n\t\t\t\t})}\n\t\t\t\t{sim.nodes.map((n, i) => {\n\t\t\t\t\tconst active =\n\t\t\t\t\t\tactiveSet == null || activeSet.size === 0 || activeSet.has(n.id);\n\t\t\t\t\tconst r = Math.min(32, 8 + n.memoryCount * 3);\n\t\t\t\t\treturn (\n\t\t\t\t\t\t<g key={`node-${i}`} opacity={active ? 1 : 0.15}>\n\t\t\t\t\t\t\t<circle\n\t\t\t\t\t\t\t\tcx={n.pos.x}\n\t\t\t\t\t\t\t\tcy={n.pos.y}\n\t\t\t\t\t\t\t\tr={r}\n\t\t\t\t\t\t\t\tfill=\"hsl(var(--primary) / 0.18)\"\n\t\t\t\t\t\t\t\tstroke=\"hsl(var(--primary))\"\n\t\t\t\t\t\t\t\tstrokeWidth={1.5}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<text\n\t\t\t\t\t\t\t\tx={n.pos.x}\n\t\t\t\t\t\t\t\ty={n.pos.y + 4}\n\t\t\t\t\t\t\t\ttextAnchor=\"middle\"\n\t\t\t\t\t\t\t\tfontSize=\"10px\"\n\t\t\t\t\t\t\t\tfill=\"hsl(var(--foreground))\"\n\t\t\t\t\t\t\t\tclassName=\"pointer-events-none\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{n.label.length > 16 ? n.label.slice(0, 15) + \"…\" : n.label}\n\t\t\t\t\t\t\t</text>\n\t\t\t\t\t\t</g>\n\t\t\t\t\t);\n\t\t\t\t})}\n\t\t\t</svg>\n\n\t\t\t<div className=\"flex items-center gap-4 text-xs text-muted-foreground\">\n\t\t\t\t<span className=\"flex items-center gap-1.5\">\n\t\t\t\t\t<span className=\"inline-block h-0.5 w-6 bg-amber-500\" /> merge\n\t\t\t\t</span>\n\t\t\t\t<span className=\"flex items-center gap-1.5\">\n\t\t\t\t\t<span className=\"inline-block h-0.5 w-6 border-t border-dashed border-green-500\" />\n\t\t\t\t\tsplit\n\t\t\t\t</span>\n\t\t\t\t<span>\n\t\t\t\t\t{data.nodes.length} nodes · {data.edges.length} curation events\n\t\t\t\t</span>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n","/**\n * dashboard-client/src/tabs/WikiTab/TopicEvolutionView.tsx — planet evolution.\n *\n * Thin shell over TopicEvolutionGraph: fetches GET /api/wiki/evolution via\n * useApi and refetches when a `wiki_rebuilt` SSE event lands (emitted by the\n * extension after a topic-model rebuild), so the graph + scrubber stay live\n * between polls. No data is fabricated — only the contract fields are used.\n *\n * Styling: Tailwind + shadcn (Card). No legacy CSS classes.\n */\n\nimport type React from \"react\";\nimport { useCallback, useEffect, useMemo } from \"react\";\nimport { useApi } from \"../../hooks/useApi\";\nimport { useSSE } from \"../../hooks/useSSE\";\nimport { fetchTopicEvolution } from \"../../api/client\";\nimport type { TopicEvolutionResponse, SseEvent } from \"@contracts\";\nimport { Card, CardContent } from \"../../components/ui/card\";\nimport TopicEvolutionGraph from \"./TopicEvolutionGraph\";\n\nfunction isWikiRebuilt(e: SseEvent): e is Extract<SseEvent, { type: \"wiki_rebuilt\" }> {\n\treturn e.type === \"wiki_rebuilt\";\n}\n\nexport default function TopicEvolutionView(): React.ReactElement {\n\tconst { data, error, loading, refetch } = useApi<TopicEvolutionResponse>(\n\t\tuseCallback(() => fetchTopicEvolution(), []),\n\t\t{ pollInterval: 60_000 },\n\t);\n\n\tconst { events } = useSSE();\n\tconst lastRebuiltTs = useMemo(() => {\n\t\tconst rebuilt = events.filter(isWikiRebuilt);\n\t\tif (rebuilt.length === 0) return null;\n\t\treturn rebuilt[rebuilt.length - 1].ts;\n\t}, [events]);\n\n\tuseEffect(() => {\n\t\tif (lastRebuiltTs != null) refetch();\n\t}, [lastRebuiltTs, refetch]);\n\n\tif (error && !data) {\n\t\treturn (\n\t\t\t<div className=\"rounded-lg border border-border bg-bg-card p-6 text-sm text-danger\">\n\t\t\t\tError loading evolution graph: {error.message}\n\t\t\t</div>\n\t\t);\n\t}\n\tif (loading && !data) {\n\t\treturn (\n\t\t\t<div className=\"rounded-lg border border-border bg-bg-card p-6 text-sm text-muted-foreground\">\n\t\t\t\tLoading evolution graph…\n\t\t\t</div>\n\t\t);\n\t}\n\n\treturn (\n\t\t<Card>\n\t\t\t<CardContent>\n\t\t\t\t<h3 className=\"mb-2 font-heading text-sm font-semibold text-muted-foreground\">\n\t\t\t\t\tTopic evolution\n\t\t\t\t</h3>\n\t\t\t\t{data ? (\n\t\t\t\t\t<TopicEvolutionGraph data={data} />\n\t\t\t\t) : (\n\t\t\t\t\t<p className=\"text-sm text-muted-foreground\">No data yet.</p>\n\t\t\t\t)}\n\t\t\t</CardContent>\n\t\t</Card>\n\t);\n}\n","/**\n * dashboard-client/src/tabs/WikiTab.tsx — Wiki Revival (W3): wiki landing list.\n *\n * Replaces TopicsTab in the App registry (TopicsTab.tsx kept, unused). Renders\n * the wiki index: each auto-categorized topic with its resolved label (edited\n * badge when user-renamed), member count, curation badges (label/merge/split),\n * and a click-to-open that pushes a client-side page state to WikiPage.\n *\n * Styling is Tailwind + shadcn (Card, Badge, Button) — no legacy CSS classes.\n */\n\nimport type React from \"react\";\nimport { useCallback, useMemo, useState } from \"react\";\nimport { useApi } from \"../hooks/useApi\";\nimport { fetchWikiIndex } from \"../api/client\";\nimport type { WikiIndexEntry, WikiIndexResponse } from \"@contracts\";\nimport { Badge } from \"../components/ui/badge\";\nimport { Button } from \"../components/ui/button\";\nimport { Toggle } from \"../components/ui/toggle\";\nimport WikiPage from \"./WikiTab/WikiPage\";\nimport TopicEvolutionView from \"./WikiTab/TopicEvolutionView\";\n\ntype WikiView = \"list\" | \"evolution\";\n\nconst KIND_BADGES: Record<string, \"default\" | \"accent\" | \"warning\"> = {\n\tlabel: \"accent\",\n\tmerge: \"warning\",\n\tsplit: \"default\",\n};\n\nfunction fmtTs(ms: number | null): string {\n\tif (!ms) return \"—\";\n\treturn new Date(ms).toLocaleString();\n}\n\nfunction ViewToggle({\n\tview,\n\tonChange,\n}: {\n\tview: WikiView;\n\tonChange: (v: WikiView) => void;\n}): React.ReactElement {\n\treturn (\n\t\t<div className=\"flex gap-1\">\n\t\t\t<Toggle pressed={view === \"list\"} onClick={() => onChange(\"list\")}>\n\t\t\t\tTopics\n\t\t\t</Toggle>\n\t\t\t<Toggle\n\t\t\t\tpressed={view === \"evolution\"}\n\t\t\t\tonClick={() => onChange(\"evolution\")}\n\t\t\t>\n\t\t\t\tEvolution\n\t\t\t</Toggle>\n\t\t</div>\n\t);\n}\n\ninterface RowProps {\n\tentry: WikiIndexEntry;\n\tonOpen: (id: string) => void;\n}\n\nfunction TopicRow({ entry, onOpen }: RowProps): React.ReactElement {\n\treturn (\n\t\t<tr className=\"border-b border-border/60 last:border-0 hover:bg-bg-elevated/40\">\n\t\t\t<td className=\"py-2 pr-3\">\n\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t<span className=\"font-medium text-foreground\">{entry.label}</span>\n\t\t\t\t\t{entry.edited && <Badge variant=\"outline\">edited</Badge>}\n\t\t\t\t</div>\n\t\t\t</td>\n\t\t\t<td className=\"py-2 pr-3 text-sm text-muted-foreground\">\n\t\t\t\t{entry.memoryCount}\n\t\t\t</td>\n\t\t\t<td className=\"py-2 pr-3\">\n\t\t\t\t<div className=\"flex flex-wrap gap-1\">\n\t\t\t\t\t{entry.overrideKinds.map((k) => (\n\t\t\t\t\t\t<Badge key={k} variant={KIND_BADGES[k] ?? \"default\"}>\n\t\t\t\t\t\t\t{k}\n\t\t\t\t\t\t</Badge>\n\t\t\t\t\t))}\n\t\t\t\t</div>\n\t\t\t</td>\n\t\t\t<td className=\"py-2 pr-3 text-xs text-muted-foreground\">\n\t\t\t\t{fmtTs(entry.lastUpdated)}\n\t\t\t</td>\n\t\t\t<td className=\"py-2 text-right\">\n\t\t\t\t<Button\n\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\tonClick={() => onOpen(entry.id)}\n\t\t\t\t>\n\t\t\t\t\tOpen\n\t\t\t\t</Button>\n\t\t\t</td>\n\t\t</tr>\n\t);\n}\n\nexport default function WikiTab(): React.ReactElement {\n\tconst { data, loading, error } = useApi<WikiIndexResponse>(\n\t\tuseCallback(() => fetchWikiIndex(), []),\n\t\t{ pollInterval: 30_000 },\n\t);\n\tconst [view, setView] = useState<WikiView>(\"list\");\n\tconst [query, setQuery] = useState(\"\");\n\tconst [openId, setOpenId] = useState<string | null>(null);\n\n\tconst filtered = useMemo(() => {\n\t\tif (!data) return [];\n\t\tconst q = query.trim().toLowerCase();\n\t\tif (!q) return data.topics;\n\t\treturn data.topics.filter((t) =>\n\t\t\tt.label.toLowerCase().includes(q),\n\t\t);\n\t}, [data, query]);\n\n\tif (openId) {\n\t\treturn <WikiPage topicId={openId} onBack={() => setOpenId(null)} />;\n\t}\n\n\tif (view === \"evolution\") {\n\t\treturn (\n\t\t\t<div className=\"flex flex-col gap-3\">\n\t\t\t\t<ViewToggle view={view} onChange={setView} />\n\t\t\t\t<TopicEvolutionView />\n\t\t\t</div>\n\t\t);\n\t}\n\n\tif (error && !data) {\n\t\treturn (\n\t\t\t<div className=\"rounded-lg border border-border bg-bg-card p-6 text-sm text-danger\">\n\t\t\t\tError loading wiki: {error.message}\n\t\t\t</div>\n\t\t);\n\t}\n\tif (loading && !data) {\n\t\treturn (\n\t\t\t<div className=\"rounded-lg border border-border bg-bg-card p-6 text-sm text-muted-foreground\">\n\t\t\t\tLoading wiki…\n\t\t\t</div>\n\t\t);\n\t}\n\tif (!data) {\n\t\treturn (\n\t\t\t<div className=\"rounded-lg border border-border bg-bg-card p-6 text-sm text-muted-foreground\">\n\t\t\t\tNo wiki data available.\n\t\t\t</div>\n\t\t);\n\t}\n\tif (data.totalTopics === 0) {\n\t\treturn (\n\t\t\t<div className=\"rounded-lg border border-border bg-bg-card p-6 text-sm text-muted-foreground\">\n\t\t\t\t<h3 className=\"mb-2 font-heading text-base text-foreground\">Wiki</h3>\n\t\t\t\t<p>\n\t\t\t\t\tNo topics yet. Topics are auto-generated after every Nth compaction\n\t\t\t\t\tfrom real memory embeddings (k-means + TF-IDF).\n\t\t\t\t</p>\n\t\t\t\t<p className=\"mt-2\">Check back after a few more compaction cycles.</p>\n\t\t\t</div>\n\t\t);\n\t}\n\n\treturn (\n\t\t<div className=\"flex flex-col gap-4\">\n\t\t\t<div className=\"flex flex-wrap items-end justify-between gap-2\">\n\t\t\t\t<div className=\"flex items-center gap-3\">\n\t\t\t\t\t<ViewToggle view={view} onChange={setView} />\n\t\t\t\t\t<div>\n\t\t\t\t\t\t<h2 className=\"font-heading text-lg font-semibold\">Wiki</h2>\n\t\t\t\t\t\t<p className=\"text-sm text-muted-foreground\">\n\t\t\t\t\t\t\t{data.totalTopics} topic{data.totalTopics !== 1 ? \"s\" : \"\"} ·{\" \"}\n\t\t\t\t\t\t\t{data.totalMemories} assigned memori\n\t\t\t\t\t\t\t{data.totalMemories !== 1 ? \"es\" : \"y\"}\n\t\t\t\t\t\t\t{data.lastRebuildAt != null && (\n\t\t\t\t\t\t\t\t<> · last rebuild {fmtTs(data.lastRebuildAt)}</>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t<input\n\t\t\t\t\ttype=\"search\"\n\t\t\t\t\tplaceholder=\"Filter topics by label…\"\n\t\t\t\t\taria-label=\"Filter topics by label\"\n\t\t\t\t\tvalue={query}\n\t\t\t\t\tonChange={(e) => setQuery(e.target.value)}\n\t\t\t\t\tclassName=\"w-64 rounded-md border border-border bg-bg-elevated/50 px-3 py-2 text-sm outline-none transition-colors focus:border-primary\"\n\t\t\t\t/>\n\t\t\t</div>\n\n\t\t\t<div className=\"overflow-x-auto rounded-lg border border-border bg-bg-card\">\n\t\t\t\t<table className=\"w-full table-fixed text-sm\">\n\t\t\t\t\t<thead>\n\t\t\t\t\t\t<tr className=\"border-b border-border text-left text-xs uppercase text-muted-foreground\">\n\t\t\t\t\t\t\t<th className=\"w-1/3 px-4 py-2 font-medium\">Label</th>\n\t\t\t\t\t\t\t<th className=\"w-16 py-2 font-medium\">Memories</th>\n\t\t\t\t\t\t\t<th className=\"w-40 py-2 font-medium\">Badges</th>\n\t\t\t\t\t\t\t<th className=\"w-40 py-2 font-medium\">Last updated</th>\n\t\t\t\t\t\t\t<th className=\"w-20 py-2\" />\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t</thead>\n\t\t\t\t\t<tbody>\n\t\t\t\t\t\t{filtered.map((t) => (\n\t\t\t\t\t\t\t<TopicRow key={t.id} entry={t} onOpen={setOpenId} />\n\t\t\t\t\t\t))}\n\t\t\t\t\t\t{filtered.length === 0 && (\n\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t\t<td\n\t\t\t\t\t\t\t\t\tcolSpan={5}\n\t\t\t\t\t\t\t\t\tclassName=\"px-4 py-6 text-center text-muted-foreground\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\tNo topics match \"{query}\".\n\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</tbody>\n\t\t\t\t</table>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n"],"names":["memberSnippet","id","method","base","WikiPageControls","topicId","others","members","onMutated","mode","setMode","useState","label","setLabel","target","setTarget","picked","setPicked","busy","setBusy","error","setError","open","m","submit","renameTopic","mergeTopics","splitTopic","e","jsxs","Fragment","jsx","Button","o","prev","x","W","H","BAR_W","GAP","fmtBucket","ts","TopicTimeline","data","useApi","useCallback","fetchTopicTimeline","buckets","total","max","b","totalWidth","scale","i","h","fmtTs","ms","METHOD_LABELS","WikiPage","onBack","dirty","setDirty","bump","d","page","loading","fetchWikiTopic","index","fetchWikiIndex","labelOf","useMemo","map","t","otherTopics","Badge","Card","CardContent","p","REPULSION","ATTRACTION","CENTER_GRAVITY","DAMPING","SPEED_LIMIT","MIN_VELOCITY","MAX_ITERATIONS","edgeColor","kind","initialLayout","nodes","n","angle","applyForces","sim","edges","fx","fy","j","dx","dy","distSq","dist","other","speed","TopicEvolutionGraph","simRef","useRef","animRef","setFrame","scrub","setScrub","useEffect","indexById","si","ti","timeByActivity","iter","tick","scrubMax","scrubBucket","idx","activeSet","a","active","r","isWikiRebuilt","TopicEvolutionView","refetch","fetchTopicEvolution","events","useSSE","lastRebuiltTs","rebuilt","KIND_BADGES","ViewToggle","view","onChange","Toggle","TopicRow","entry","onOpen","k","WikiTab","setView","query","setQuery","openId","setOpenId","filtered","q"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"WikiTab-1LcQrXk3.js","sources":["../../src/tabs/WikiTab/WikiPageControls.tsx","../../src/tabs/WikiTab/TopicTimeline.tsx","../../src/tabs/WikiTab/WikiPage.tsx","../../src/tabs/WikiTab/TopicEvolutionGraph.tsx","../../src/tabs/WikiTab/TopicEvolutionView.tsx","../../src/tabs/WikiTab.tsx"],"sourcesContent":["/**\n * dashboard-client/src/tabs/WikiTab/WikiPageControls.tsx — rename/merge/split.\n *\n * Controlled dialogs for the three curation mutations. Non-destructive: on\n * success the parent refetches and paints the returned CurationResult; on\n * failure the pre-mutation snapshot stays (no local partial apply).\n *\n * Styling: Tailwind + shadcn (Button). No legacy CSS classes.\n */\n\nimport type React from \"react\";\nimport { useState } from \"react\";\nimport { renameTopic, mergeTopics, splitTopic } from \"../../api/client\";\nimport type { MemoryProvenance } from \"@contracts\";\nimport { Button } from \"../../components/ui/button\";\n\ninterface ControlsProps {\n\ttopicId: string;\n\tothers: Array<{ id: string; label: string }>;\n\tmembers: MemoryProvenance[];\n\tonMutated: () => void;\n}\n\ntype Mode = \"rename\" | \"merge\" | \"split\" | null;\n\nfunction memberSnippet(id: string, method: string): string {\n\tconst base = id.length > 14 ? id.slice(0, 14) + \"…\" : id;\n\treturn method ? `${base} (${method})` : base;\n}\n\nexport default function WikiPageControls({\n\ttopicId,\n\tothers,\n\tmembers,\n\tonMutated,\n}: ControlsProps): React.ReactElement {\n\tconst [mode, setMode] = useState<Mode>(null);\n\tconst [label, setLabel] = useState(\"\");\n\tconst [target, setTarget] = useState(\"\");\n\tconst [picked, setPicked] = useState<string[]>([]);\n\tconst [busy, setBusy] = useState(false);\n\tconst [error, setError] = useState<string | null>(null);\n\n\tfunction open(m: Exclude<Mode, null>): void {\n\t\tsetMode(m);\n\t\tsetError(null);\n\t\tsetLabel(\"\");\n\t\tsetTarget(\"\");\n\t\tsetPicked([]);\n\t}\n\n\tasync function submit(): Promise<void> {\n\t\tsetBusy(true);\n\t\tsetError(null);\n\t\ttry {\n\t\t\tif (mode === \"rename\") {\n\t\t\t\tawait renameTopic(topicId, label);\n\t\t\t} else if (mode === \"merge\") {\n\t\t\t\tif (!target) {\n\t\t\t\t\tsetError(\"Pick a target topic.\");\n\t\t\t\t\tsetBusy(false);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tawait mergeTopics(topicId, target);\n\t\t\t} else if (mode === \"split\") {\n\t\t\t\tif (picked.length === 0) {\n\t\t\t\t\tsetError(\"Pick at least one memory.\");\n\t\t\t\t\tsetBusy(false);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tawait splitTopic(topicId, picked);\n\t\t\t}\n\t\t\tsetMode(null);\n\t\t\tonMutated();\n\t\t} catch (e) {\n\t\t\tsetError(e instanceof Error ? e.message : String(e));\n\t\t} finally {\n\t\t\tsetBusy(false);\n\t\t}\n\t}\n\n\treturn (\n\t\t<>\n\t\t\t<div className=\"flex gap-2\">\n\t\t\t\t<Button variant=\"outline\" size=\"sm\" onClick={() => open(\"rename\")}>\n\t\t\t\t\tRename\n\t\t\t\t</Button>\n\t\t\t\t<Button variant=\"outline\" size=\"sm\" onClick={() => open(\"merge\")}>\n\t\t\t\t\tMerge into…\n\t\t\t\t</Button>\n\t\t\t\t<Button variant=\"outline\" size=\"sm\" onClick={() => open(\"split\")}>\n\t\t\t\t\tSplit\n\t\t\t\t</Button>\n\t\t\t</div>\n\n\t\t\t{mode && (\n\t\t\t\t<div\n\t\t\t\t\tclassName=\"fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4\"\n\t\t\t\t\tonClick={() => setMode(null)}\n\t\t\t\t>\n\t\t\t\t\t<div\n\t\t\t\t\t\tclassName=\"w-full max-w-md rounded-lg border border-border bg-bg-card p-5 shadow-xl\"\n\t\t\t\t\t\trole=\"dialog\"\n\t\t\t\t\t\taria-modal=\"true\"\n\t\t\t\t\t\taria-label={mode}\n\t\t\t\t\t\tonClick={(e) => e.stopPropagation()}\n\t\t\t\t\t>\n\t\t\t\t\t\t<h3 className=\"mb-3 font-heading text-base font-semibold text-foreground\">\n\t\t\t\t\t\t\t{mode === \"rename\"\n\t\t\t\t\t\t\t\t? \"Rename topic\"\n\t\t\t\t\t\t\t\t: mode === \"merge\"\n\t\t\t\t\t\t\t\t\t? \"Merge into…\"\n\t\t\t\t\t\t\t\t\t: \"Split topic\"}\n\t\t\t\t\t\t</h3>\n\n\t\t\t\t\t\t{mode === \"rename\" && (\n\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\ttype=\"text\"\n\t\t\t\t\t\t\t\tvalue={label}\n\t\t\t\t\t\t\t\tplaceholder=\"New label (empty = back to auto)\"\n\t\t\t\t\t\t\t\taria-label=\"New topic label\"\n\t\t\t\t\t\t\t\tautoFocus\n\t\t\t\t\t\t\t\tonChange={(e) => setLabel(e.target.value)}\n\t\t\t\t\t\t\t\tclassName=\"w-full rounded-md border border-border bg-bg-elevated/50 px-3 py-2 text-sm outline-none transition-colors focus:border-primary\"\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t{mode === \"merge\" && (\n\t\t\t\t\t\t\t<select\n\t\t\t\t\t\t\t\tvalue={target}\n\t\t\t\t\t\t\t\taria-label=\"Target topic\"\n\t\t\t\t\t\t\t\tonChange={(e) => setTarget(e.target.value)}\n\t\t\t\t\t\t\t\tclassName=\"w-full rounded-md border border-border bg-bg-elevated/50 px-3 py-2 text-sm outline-none transition-colors focus:border-primary\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<option value=\"\">Pick a target topic…</option>\n\t\t\t\t\t\t\t\t{others.map((o) => (\n\t\t\t\t\t\t\t\t\t<option key={o.id} value={o.id}>\n\t\t\t\t\t\t\t\t\t\t{o.label}\n\t\t\t\t\t\t\t\t\t</option>\n\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t</select>\n\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t{mode === \"split\" && (\n\t\t\t\t\t\t\t<div className=\"max-h-56 overflow-auto rounded-md border border-border/60\">\n\t\t\t\t\t\t\t\t{members.map((m) => (\n\t\t\t\t\t\t\t\t\t<label\n\t\t\t\t\t\t\t\t\t\tkey={m.memoryId}\n\t\t\t\t\t\t\t\t\t\tclassName=\"flex cursor-pointer items-center gap-2 border-b border-border/40 px-3 py-2 text-sm last:border-0 hover:bg-bg-elevated/40\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\t\t\t\t\t\tchecked={picked.includes(m.memoryId)}\n\t\t\t\t\t\t\t\t\t\t\tonChange={(e) => {\n\t\t\t\t\t\t\t\t\t\t\t\tsetPicked((prev) =>\n\t\t\t\t\t\t\t\t\t\t\t\t\te.target.checked\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t? [...prev, m.memoryId]\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t: prev.filter((x) => x !== m.memoryId),\n\t\t\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\tclassName=\"accent-primary\"\n\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t<span className=\"font-mono text-xs text-muted-foreground\">\n\t\t\t\t\t\t\t\t\t\t\t{memberSnippet(m.memoryId, m.method)}\n\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t{error && (\n\t\t\t\t\t\t\t<p className=\"mt-3 text-sm text-danger\">{error}</p>\n\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t<div className=\"mt-4 flex justify-end gap-2\">\n\t\t\t\t\t\t\t<Button variant=\"ghost\" size=\"sm\" onClick={() => setMode(null)}>\n\t\t\t\t\t\t\t\tCancel\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t<Button size=\"sm\" onClick={submit} disabled={busy}>\n\t\t\t\t\t\t\t\t{busy ? \"…\" : \"Confirm\"}\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</>\n\t);\n}\n","/**\n * dashboard-client/src/tabs/WikiTab/TopicTimeline.tsx — per-topic timeline.\n *\n * Renders topic memory-addition buckets as a lightweight SVG bar strip (no\n * chart library needed). X = time bucket, bar height = memories added.\n * Fetches GET /api/wiki/topic/:topicId/timeline.\n *\n * Styling: Tailwind + shadcn (Card). No legacy CSS classes.\n */\n\nimport type React from \"react\";\nimport { useCallback } from \"react\";\nimport { useApi } from \"../../hooks/useApi\";\nimport { fetchTopicTimeline } from \"../../api/client\";\nimport type { TopicTimelineResponse } from \"@contracts\";\n\nconst W = 720;\nconst H = 56;\nconst BAR_W = 10;\nconst GAP = 3;\n\ninterface Props {\n\ttopicId: string;\n}\n\nfunction fmtBucket(ts: number): string {\n\tif (!ts) return \"—\";\n\treturn new Date(ts).toLocaleDateString();\n}\n\nexport default function TopicTimeline({ topicId }: Props): React.ReactElement {\n\tconst { data, error } = useApi<TopicTimelineResponse>(\n\t\tuseCallback(() => fetchTopicTimeline(topicId), [topicId]),\n\t\t{},\n\t);\n\n\tif (error) {\n\t\treturn (\n\t\t\t<div className=\"rounded-lg border border-border bg-bg-card p-4 text-sm text-muted-foreground\">\n\t\t\t\tTimeline unavailable.\n\t\t\t</div>\n\t\t);\n\t}\n\tif (!data) {\n\t\treturn (\n\t\t\t<div className=\"rounded-lg border border-border bg-bg-card p-4 text-sm text-muted-foreground\">\n\t\t\t\tLoading timeline…\n\t\t\t</div>\n\t\t);\n\t}\n\n\tconst { buckets, total } = data;\n\tconst max = Math.max(...buckets.map((b) => b.count), 1);\n\tconst totalWidth = buckets.length * (BAR_W + GAP);\n\tconst scale = Math.min(1, (W - 24) / Math.max(totalWidth, 1));\n\n\treturn (\n\t\t<div className=\"rounded-lg border border-border bg-bg-card p-4\">\n\t\t\t<h3 className=\"mb-2 font-heading text-sm font-semibold text-muted-foreground\">\n\t\t\t\tMemory timeline{\" \"}\n\t\t\t\t<span className=\"font-normal text-xs\">\n\t\t\t\t\t({total} memory{total !== 1 ? \"ies\" : \"y\"} · daily buckets)\n\t\t\t\t</span>\n\t\t\t</h3>\n\t\t\t{buckets.length === 0 ? (\n\t\t\t\t<p className=\"text-sm text-muted-foreground\">No timeline data yet.</p>\n\t\t\t) : (\n\t\t\t\t<>\n\t\t\t\t\t<svg\n\t\t\t\t\t\twidth=\"100%\"\n\t\t\t\t\t\theight={H}\n\t\t\t\t\t\tviewBox={`0 0 ${Math.max(W, totalWidth * scale)} ${H}`}\n\t\t\t\t\t\trole=\"img\"\n\t\t\t\t\t\taria-label=\"Memory additions over time\"\n\t\t\t\t\t>\n\t\t\t\t\t\t{buckets.map((b, i) => {\n\t\t\t\t\t\t\tconst x = i * (BAR_W + GAP) * scale;\n\t\t\t\t\t\t\tconst h = Math.max(3, (b.count / max) * (H - 20));\n\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t<rect\n\t\t\t\t\t\t\t\t\tkey={`${b.bucket}-${i}`}\n\t\t\t\t\t\t\t\t\tx={x}\n\t\t\t\t\t\t\t\t\ty={H - 6 - h}\n\t\t\t\t\t\t\t\t\twidth={Math.max(2, BAR_W * scale)}\n\t\t\t\t\t\t\t\t\theight={h}\n\t\t\t\t\t\t\t\t\trx={2}\n\t\t\t\t\t\t\t\t\tfill=\"hsl(var(--primary))\"\n\t\t\t\t\t\t\t\t\topacity={0.85}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<title>{`${fmtBucket(b.bucket)}: ${b.count}`}</title>\n\t\t\t\t\t\t\t\t</rect>\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t})}\n\t\t\t\t\t</svg>\n\t\t\t\t\t<p className=\"mt-1 text-xs text-muted-foreground\">\n\t\t\t\t\t\tFirst display: {fmtBucket(buckets[0].bucket)} · last:\n\t\t\t\t\t\t{fmtBucket(buckets[buckets.length - 1].bucket)}\n\t\t\t\t\t</p>\n\t\t\t\t</>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n","/**\n * dashboard-client/src/tabs/WikiTab/WikiPage.tsx — single wiki topic page.\n *\n * Renders the extractive summary, key member memories, the member provenance\n * table (memory → session/assigned-at/method), related topics (resolved from\n * the wiki index), and the per-topic memory timeline. Mutations\n * (rename/merge/split) delegate to WikiPageControls; on mutation success the\n * page + index are refetched.\n *\n * Styling: Tailwind + shadcn (Card, Badge, Button) — no legacy CSS.\n */\n\nimport type React from \"react\";\nimport { useCallback, useMemo, useState } from \"react\";\nimport { useApi } from \"../../hooks/useApi\";\nimport { fetchWikiTopic, fetchWikiIndex } from \"../../api/client\";\nimport type { WikiPageResponse, WikiIndexResponse } from \"@contracts\";\nimport { Badge } from \"../../components/ui/badge\";\nimport { Button } from \"../../components/ui/button\";\nimport { Card, CardContent } from \"../../components/ui/card\";\nimport WikiPageControls from \"./WikiPageControls\";\nimport TopicTimeline from \"./TopicTimeline\";\n\ninterface WikiPageProps {\n\ttopicId: string;\n\tonBack: () => void;\n}\n\nfunction fmtTs(ms: number): string {\n\tif (!ms) return \"—\";\n\treturn new Date(ms).toLocaleString();\n}\n\nconst METHOD_LABELS: Record<string, string> = {\n\t\"kmeans+tfidf\": \"kmeans\",\n\tmerge: \"merge\",\n\tsplit: \"split\",\n\tmanual: \"manual\",\n};\n\nexport default function WikiPage({\n\ttopicId,\n\tonBack,\n}: WikiPageProps): React.ReactElement {\n\tconst [dirty, setDirty] = useState(0);\n\tconst bump = useCallback(() => setDirty((d) => d + 1), []);\n\n\tconst { data: page, error, loading } = useApi<WikiPageResponse>(\n\t\tuseCallback(() => fetchWikiTopic(topicId), [topicId, dirty]),\n\t\t{},\n\t);\n\tconst { data: index } = useApi<WikiIndexResponse>(\n\t\tuseCallback(() => fetchWikiIndex(), []),\n\t\t{},\n\t);\n\n\tconst labelOf = useMemo(() => {\n\t\tconst map = new Map<string, string>();\n\t\tfor (const t of index?.topics ?? []) map.set(t.id, t.label);\n\t\treturn map;\n\t}, [index]);\n\n\tif (error && !page) {\n\t\treturn (\n\t\t\t<div className=\"rounded-lg border border-border bg-bg-card p-6 text-sm text-danger\">\n\t\t\t\tError loading topic: {error.message}\n\t\t\t</div>\n\t\t);\n\t}\n\tif (loading && !page) {\n\t\treturn (\n\t\t\t<div className=\"rounded-lg border border-border bg-bg-card p-6 text-sm text-muted-foreground\">\n\t\t\t\tLoading topic…\n\t\t\t</div>\n\t\t);\n\t}\n\tif (!page) {\n\t\treturn (\n\t\t\t<div className=\"rounded-lg border border-border bg-bg-card p-6 text-sm text-muted-foreground\">\n\t\t\t\tNo topic data available.\n\t\t\t</div>\n\t\t);\n\t}\n\n\tconst otherTopics = (index?.topics ?? [])\n\t\t.filter((t) => t.id !== topicId)\n\t\t.map((t) => ({ id: t.id, label: t.label }));\n\n\treturn (\n\t\t<div className=\"flex flex-col gap-4\">\n\t\t\t<div className=\"flex items-center justify-between\">\n\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t<Button variant=\"outline\" size=\"sm\" onClick={onBack}>\n\t\t\t\t\t\t← Wiki\n\t\t\t\t\t</Button>\n\t\t\t\t\t<h2 className=\"font-heading text-lg font-semibold\">\n\t\t\t\t\t\t{page.topic.label}\n\t\t\t\t\t</h2>\n\t\t\t\t\t{page.topic.edited && <Badge variant=\"outline\">edited</Badge>}\n\t\t\t\t\t<Badge variant=\"default\">\n\t\t\t\t\t\t{page.provenance.length} memories\n\t\t\t\t\t</Badge>\n\t\t\t\t</div>\n\t\t\t\t<WikiPageControls\n\t\t\t\t\ttopicId={topicId}\n\t\t\t\t\tothers={otherTopics}\n\t\t\t\t\tmembers={page.provenance}\n\t\t\t\t\tonMutated={bump}\n\t\t\t\t/>\n\t\t\t</div>\n\n\t\t\t{page.summary && (\n\t\t\t\t<Card>\n\t\t\t\t\t<CardContent>\n\t\t\t\t\t\t<h3 className=\"mb-1 font-heading text-sm font-semibold text-muted-foreground\">\n\t\t\t\t\t\t\tSummary\n\t\t\t\t\t\t</h3>\n\t\t\t\t\t\t<p className=\"text-sm leading-relaxed text-foreground/90\">\n\t\t\t\t\t\t\t{page.summary}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t</CardContent>\n\t\t\t\t</Card>\n\t\t\t)}\n\n\t\t\t{page.keyMemories.length > 0 && (\n\t\t\t\t<Card>\n\t\t\t\t\t<CardContent>\n\t\t\t\t\t\t<h3 className=\"mb-2 font-heading text-sm font-semibold text-muted-foreground\">\n\t\t\t\t\t\t\tKey memories\n\t\t\t\t\t\t</h3>\n\t\t\t\t\t\t<ul className=\"flex flex-col gap-2\">\n\t\t\t\t\t\t\t{page.keyMemories.map((m) => (\n\t\t\t\t\t\t\t\t<li\n\t\t\t\t\t\t\t\t\tkey={m.memoryId}\n\t\t\t\t\t\t\t\t\tclassName=\"rounded-md border border-border/60 bg-bg-elevated/40 p-3 text-sm\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<p className=\"line-clamp-3 text-foreground/90\">{m.content}</p>\n\t\t\t\t\t\t\t\t\t<p className=\"mt-1 text-xs text-muted-foreground\">\n\t\t\t\t\t\t\t\t\t\t{m.memoryId} · {fmtTs(m.timestamp)}\n\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</ul>\n\t\t\t\t\t</CardContent>\n\t\t\t\t</Card>\n\t\t\t)}\n\n\t\t\t<TopicTimeline topicId={topicId} />\n\n\t\t\t{page.provenance.length > 0 && (\n\t\t\t\t<Card>\n\t\t\t\t\t<CardContent>\n\t\t\t\t\t\t<h3 className=\"mb-2 font-heading text-sm font-semibold text-muted-foreground\">\n\t\t\t\t\t\t\tProvenance\n\t\t\t\t\t\t</h3>\n\t\t\t\t\t\t<div className=\"max-h-72 overflow-auto rounded-md border border-border/60\">\n\t\t\t\t\t\t\t<table className=\"w-full text-sm\">\n\t\t\t\t\t\t\t\t<thead className=\"sticky top-0 bg-bg-card\">\n\t\t\t\t\t\t\t\t\t<tr className=\"border-b border-border text-left text-xs uppercase text-muted-foreground\">\n\t\t\t\t\t\t\t\t\t\t<th className=\"px-3 py-2 font-medium\">Memory</th>\n\t\t\t\t\t\t\t\t\t\t<th className=\"px-3 py-2 font-medium\">Session</th>\n\t\t\t\t\t\t\t\t\t\t<th className=\"px-3 py-2 font-medium\">Assigned</th>\n\t\t\t\t\t\t\t\t\t\t<th className=\"px-3 py-2 font-medium\">Method</th>\n\t\t\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t\t</thead>\n\t\t\t\t\t\t\t\t<tbody>\n\t\t\t\t\t\t\t\t\t{page.provenance.map((p) => (\n\t\t\t\t\t\t\t\t\t\t<tr\n\t\t\t\t\t\t\t\t\t\t\tkey={p.memoryId}\n\t\t\t\t\t\t\t\t\t\t\tclassName=\"border-b border-border/40 last:border-0\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t<td className=\"px-3 py-1.5 font-mono text-xs\">\n\t\t\t\t\t\t\t\t\t\t\t\t{p.memoryId}\n\t\t\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t\t\t<td className=\"px-3 py-1.5 text-xs text-muted-foreground\">\n\t\t\t\t\t\t\t\t\t\t\t\t{p.sessionId || \"unknown\"}\n\t\t\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t\t\t<td className=\"px-3 py-1.5 text-xs text-muted-foreground\">\n\t\t\t\t\t\t\t\t\t\t\t\t{fmtTs(p.assignedAt)}\n\t\t\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t\t\t<td className=\"px-3 py-1.5\">\n\t\t\t\t\t\t\t\t\t\t\t\t<Badge variant=\"outline\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t{METHOD_LABELS[p.method] ?? p.method}\n\t\t\t\t\t\t\t\t\t\t\t\t</Badge>\n\t\t\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t\t</tbody>\n\t\t\t\t\t\t\t</table>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</CardContent>\n\t\t\t\t</Card>\n\t\t\t)}\n\n\t\t\t{page.relatedTopicIds.length > 0 && (\n\t\t\t\t<Card>\n\t\t\t\t\t<CardContent>\n\t\t\t\t\t\t<h3 className=\"mb-2 font-heading text-sm font-semibold text-muted-foreground\">\n\t\t\t\t\t\t\tRelated topics\n\t\t\t\t\t\t</h3>\n\t\t\t\t\t\t<div className=\"flex flex-wrap gap-2\">\n\t\t\t\t\t\t\t{page.relatedTopicIds.map((id) => (\n\t\t\t\t\t\t\t\t<Badge key={id} variant=\"accent\">\n\t\t\t\t\t\t\t\t\t{labelOf.get(id) ?? id}\n\t\t\t\t\t\t\t\t</Badge>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</CardContent>\n\t\t\t\t</Card>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n","/**\n * dashboard-client/src/tabs/WikiTab/TopicEvolutionGraph.tsx — D3 evolution.\n *\n * Force-directed graph of wiki topics over time. Nodes are topics (radius scales\n * with memoryCount); edges are merge/split curation events. A bucket scrubber\n * (from the response buckets) dims nodes that had no activity up to the selected\n * time window — a lightweight \"growth over time\" view.\n *\n * Self-contained force simulation (Coulomb repulsion + Hooke attraction +\n * center gravity) — the codebase deliberately avoids external graph libraries\n * (see memory-map-layout.ts). No data is fabricated: only real node/edge/bucket\n * fields from the contract are used. Styling is Tailwind.\n */\n\nimport type React from \"react\";\nimport { useEffect, useMemo, useRef, useState } from \"react\";\nimport type { TopicEvolutionResponse } from \"@contracts\";\n\n// ── Simulation constants ────────────────────────────────────────────────\nconst W = 900;\nconst H = 560;\nconst REPULSION = 60_000;\nconst ATTRACTION = 0.012;\nconst CENTER_GRAVITY = 0.012;\nconst DAMPING = 0.8;\nconst SPEED_LIMIT = 2;\nconst MIN_VELOCITY = 0.02;\nconst MAX_ITERATIONS = 250;\n\ninterface SimNode {\n\tid: string;\n\tlabel: string;\n\tmemoryCount: number;\n\tpos: { x: number; y: number };\n\tvx: number;\n\tvy: number;\n}\n\ninterface SimEdge {\n\tsource: number;\n\ttarget: number;\n\tkind: \"merge\" | \"split\";\n}\n\nfunction edgeColor(kind: \"merge\" | \"split\"): string {\n\treturn kind === \"merge\" ? \"#f59e0b\" : \"#22c55e\"; // amber / green\n}\n\nfunction initialLayout(nodes: TopicEvolutionResponse[\"nodes\"]): SimNode[] {\n\treturn nodes.map((n, i) => {\n\t\tconst angle = (2 * Math.PI * i) / Math.max(nodes.length, 1);\n\t\treturn {\n\t\t\tid: n.id,\n\t\t\tlabel: n.label,\n\t\t\tmemoryCount: n.memoryCount,\n\t\t\tpos: { x: Math.cos(angle) * 220 + W / 2, y: Math.sin(angle) * 220 + H / 2 },\n\t\t\tvx: 0,\n\t\t\tvy: 0,\n\t\t};\n\t});\n}\n\nfunction applyForces(sim: { nodes: SimNode[]; edges: SimEdge[] }): void {\n\tconst { nodes, edges } = sim;\n\tfor (let i = 0; i < nodes.length; i++) {\n\t\tconst n = nodes[i];\n\t\tlet fx = 0;\n\t\tlet fy = 0;\n\t\tfor (let j = 0; j < nodes.length; j++) {\n\t\t\tif (i === j) continue;\n\t\t\tconst dx = n.pos.x - nodes[j].pos.x;\n\t\t\tconst dy = n.pos.y - nodes[j].pos.y;\n\t\t\tconst distSq = dx * dx + dy * dy || 1;\n\t\t\tconst dist = Math.sqrt(distSq);\n\t\t\tfx += (dx / dist) * (REPULSION / distSq);\n\t\t\tfy += (dy / dist) * (REPULSION / distSq);\n\t\t}\n\t\tfor (const e of edges) {\n\t\t\tlet other: number;\n\t\t\tif (e.source === i) other = e.target;\n\t\t\telse if (e.target === i) other = e.source;\n\t\t\telse continue;\n\t\t\tconst dx = nodes[other].pos.x - n.pos.x;\n\t\t\tconst dy = nodes[other].pos.y - n.pos.y;\n\t\t\tfx += dx * ATTRACTION;\n\t\t\tfy += dy * ATTRACTION;\n\t\t}\n\t\tfx -= n.pos.x * CENTER_GRAVITY;\n\t\tfy -= n.pos.y * CENTER_GRAVITY;\n\t\tn.vx = (n.vx + fx) * DAMPING;\n\t\tn.vy = (n.vy + fy) * DAMPING;\n\t\tconst speed = Math.sqrt(n.vx * n.vx + n.vy * n.vy);\n\t\tif (speed > SPEED_LIMIT) {\n\t\t\tn.vx = (n.vx / speed) * SPEED_LIMIT;\n\t\t\tn.vy = (n.vy / speed) * SPEED_LIMIT;\n\t\t}\n\t\tif (speed > MIN_VELOCITY) {\n\t\t\tn.pos.x += n.vx;\n\t\t\tn.pos.y += n.vy;\n\t\t}\n\t}\n}\n\ninterface Props {\n\tdata: TopicEvolutionResponse;\n}\n\nexport default function TopicEvolutionGraph({\n\tdata,\n}: Props): React.ReactElement {\n\tconst simRef = useRef<{\n\t\tnodes: SimNode[];\n\t\tedges: SimEdge[];\n\t\ttimeByActivity: Map<string, number> | null;\n\t} | null>(null);\n\tconst animRef = useRef(0);\n\tconst [, setFrame] = useState(0);\n\tconst [scrub, setScrub] = useState<number>(0);\n\n\t// Initialize simulation from data (re-run when data changes).\n\tuseEffect(() => {\n\t\tconst nodes = initialLayout(data.nodes);\n\t\tconst indexById = new Map(nodes.map((n, i) => [n.id, i]));\n\t\tconst edges: SimEdge[] = data.edges\n\t\t\t.map((e) => {\n\t\t\t\tconst si = indexById.get(e.source);\n\t\t\t\tconst ti = indexById.get(e.target);\n\t\t\t\tif (si === undefined || ti === undefined || si === ti) return null;\n\t\t\t\treturn { source: si, target: ti, kind: e.kind };\n\t\t\t})\n\t\t\t.filter((e): e is SimEdge => e !== null);\n\t\t// Latest activity per node (from merge/split edge timestamps).\n\t\tconst timeByActivity = new Map<string, number>();\n\t\tfor (const e of data.edges) {\n\t\t\ttimeByActivity.set(e.source, Math.max(timeByActivity.get(e.source) ?? 0, e.at));\n\t\t\ttimeByActivity.set(e.target, Math.max(timeByActivity.get(e.target) ?? 0, e.at));\n\t\t}\n\t\tsimRef.current = { nodes, edges, timeByActivity };\n\t\tsetFrame(1);\n\t\treturn () => cancelAnimationFrame(animRef.current);\n\t}, [data]);\n\n\t// Run the force simulation to stable layout.\n\tuseEffect(() => {\n\t\tif (!simRef.current) return;\n\t\tlet iter = 0;\n\t\tconst tick = () => {\n\t\t\tif (!simRef.current || iter >= MAX_ITERATIONS) return;\n\t\t\tapplyForces(simRef.current);\n\t\t\titer++;\n\t\t\tsetFrame(iter);\n\t\t\tanimRef.current = requestAnimationFrame(tick);\n\t\t};\n\t\tanimRef.current = requestAnimationFrame(tick);\n\t\treturn () => cancelAnimationFrame(animRef.current);\n\t}, [data]);\n\n\tconst sim = simRef.current;\n\n\tconst scrubMax = Math.max(data.buckets.length - 1, 0);\n\tconst scrubBucket = useMemo(() => {\n\t\tif (data.buckets.length === 0) return null;\n\t\tconst idx = Math.min(scrub, data.buckets.length - 1);\n\t\treturn data.buckets[idx].bucket;\n\t}, [scrub, data.buckets]);\n\n\tif (!sim || data.nodes.length === 0) {\n\t\treturn (\n\t\t\t<div className=\"rounded-lg border border-border bg-bg-card p-6 text-sm text-muted-foreground\">\n\t\t\t\tNo evolution data yet. Merge or split topics to see connections, then\n\t\t\t\tcheck back after a rebuild.\n\t\t\t</div>\n\t\t);\n\t}\n\n\tconst activeSet =\n\t\tscrubBucket == null || !sim.timeByActivity\n\t\t\t? null\n\t\t\t: new Set(\n\t\t\t\t\t[...sim.timeByActivity.entries()]\n\t\t\t\t\t\t.filter(([, t]) => t <= scrubBucket)\n\t\t\t\t\t\t.map(([id]) => id),\n\t\t\t\t);\n\n\treturn (\n\t\t<div className=\"flex flex-col gap-3\">\n\t\t\t{/* Scrubber */}\n\t\t\t{data.buckets.length > 1 && (\n\t\t\t\t<div className=\"flex items-center gap-3\">\n\t\t\t\t\t<input\n\t\t\t\t\t\ttype=\"range\"\n\t\t\t\t\t\tmin={0}\n\t\t\t\t\t\tmax={scrubMax}\n\t\t\t\t\t\tvalue={scrub}\n\t\t\t\t\t\tonChange={(e) => setScrub(Number(e.target.value))}\n\t\t\t\t\t\tclassName=\"w-full accent-primary\"\n\t\t\t\t\t\taria-label=\"Scrub through topic activity time\"\n\t\t\t\t\t/>\n\t\t\t\t\t<span className=\"whitespace-nowrap text-xs text-muted-foreground\">\n\t\t\t\t\t\t{scrubBucket ? new Date(scrubBucket).toLocaleDateString() : \"all\"}\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{/* SVG graph */}\n\t\t\t<svg\n\t\t\t\twidth=\"100%\"\n\t\t\t\theight={H}\n\t\t\t\tviewBox={`0 0 ${W} ${H}`}\n\t\t\t\trole=\"img\"\n\t\t\t\taria-label=\"Topic evolution graph\"\n\t\t\t\tclassName=\"rounded-lg border border-border/60 bg-bg-card\"\n\t\t\t>\n\t\t\t\t{sim.edges.map((e, i) => {\n\t\t\t\t\tconst a = sim.nodes[e.source];\n\t\t\t\t\tconst b = sim.nodes[e.target];\n\t\t\t\t\treturn (\n\t\t\t\t\t\t<line\n\t\t\t\t\t\t\tkey={`edge-${i}`}\n\t\t\t\t\t\t\tx1={a.pos.x}\n\t\t\t\t\t\t\ty1={a.pos.y}\n\t\t\t\t\t\t\tx2={b.pos.x}\n\t\t\t\t\t\t\ty2={b.pos.y}\n\t\t\t\t\t\t\tstroke={edgeColor(e.kind)}\n\t\t\t\t\t\t\tstrokeWidth={1.5}\n\t\t\t\t\t\t\tstrokeDasharray={e.kind === \"split\" ? \"4 3\" : undefined}\n\t\t\t\t\t\t\topacity={0.5}\n\t\t\t\t\t\t/>\n\t\t\t\t\t);\n\t\t\t\t})}\n\t\t\t\t{sim.nodes.map((n, i) => {\n\t\t\t\t\tconst active =\n\t\t\t\t\t\tactiveSet == null || activeSet.size === 0 || activeSet.has(n.id);\n\t\t\t\t\tconst r = Math.min(32, 8 + n.memoryCount * 3);\n\t\t\t\t\treturn (\n\t\t\t\t\t\t<g key={`node-${i}`} opacity={active ? 1 : 0.15}>\n\t\t\t\t\t\t\t<circle\n\t\t\t\t\t\t\t\tcx={n.pos.x}\n\t\t\t\t\t\t\t\tcy={n.pos.y}\n\t\t\t\t\t\t\t\tr={r}\n\t\t\t\t\t\t\t\tfill=\"hsl(var(--primary) / 0.18)\"\n\t\t\t\t\t\t\t\tstroke=\"hsl(var(--primary))\"\n\t\t\t\t\t\t\t\tstrokeWidth={1.5}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<text\n\t\t\t\t\t\t\t\tx={n.pos.x}\n\t\t\t\t\t\t\t\ty={n.pos.y + 4}\n\t\t\t\t\t\t\t\ttextAnchor=\"middle\"\n\t\t\t\t\t\t\t\tfontSize=\"10px\"\n\t\t\t\t\t\t\t\tfill=\"hsl(var(--foreground))\"\n\t\t\t\t\t\t\t\tclassName=\"pointer-events-none\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{n.label.length > 16 ? n.label.slice(0, 15) + \"…\" : n.label}\n\t\t\t\t\t\t\t</text>\n\t\t\t\t\t\t</g>\n\t\t\t\t\t);\n\t\t\t\t})}\n\t\t\t</svg>\n\n\t\t\t<div className=\"flex items-center gap-4 text-xs text-muted-foreground\">\n\t\t\t\t<span className=\"flex items-center gap-1.5\">\n\t\t\t\t\t<span className=\"inline-block h-0.5 w-6 bg-amber-500\" /> merge\n\t\t\t\t</span>\n\t\t\t\t<span className=\"flex items-center gap-1.5\">\n\t\t\t\t\t<span className=\"inline-block h-0.5 w-6 border-t border-dashed border-green-500\" />\n\t\t\t\t\tsplit\n\t\t\t\t</span>\n\t\t\t\t<span>\n\t\t\t\t\t{data.nodes.length} nodes · {data.edges.length} curation events\n\t\t\t\t</span>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n","/**\n * dashboard-client/src/tabs/WikiTab/TopicEvolutionView.tsx — planet evolution.\n *\n * Thin shell over TopicEvolutionGraph: fetches GET /api/wiki/evolution via\n * useApi and refetches when a `wiki_rebuilt` SSE event lands (emitted by the\n * extension after a topic-model rebuild), so the graph + scrubber stay live\n * between polls. No data is fabricated — only the contract fields are used.\n *\n * Styling: Tailwind + shadcn (Card). No legacy CSS classes.\n */\n\nimport type React from \"react\";\nimport { useCallback, useEffect, useMemo } from \"react\";\nimport { useApi } from \"../../hooks/useApi\";\nimport { useSSE } from \"../../hooks/useSSE\";\nimport { fetchTopicEvolution } from \"../../api/client\";\nimport type { TopicEvolutionResponse, SseEvent } from \"@contracts\";\nimport { Card, CardContent } from \"../../components/ui/card\";\nimport TopicEvolutionGraph from \"./TopicEvolutionGraph\";\n\nfunction isWikiRebuilt(e: SseEvent): e is Extract<SseEvent, { type: \"wiki_rebuilt\" }> {\n\treturn e.type === \"wiki_rebuilt\";\n}\n\nexport default function TopicEvolutionView(): React.ReactElement {\n\tconst { data, error, loading, refetch } = useApi<TopicEvolutionResponse>(\n\t\tuseCallback(() => fetchTopicEvolution(), []),\n\t\t{ pollInterval: 60_000 },\n\t);\n\n\tconst { events } = useSSE();\n\tconst lastRebuiltTs = useMemo(() => {\n\t\tconst rebuilt = events.filter(isWikiRebuilt);\n\t\tif (rebuilt.length === 0) return null;\n\t\treturn rebuilt[rebuilt.length - 1].ts;\n\t}, [events]);\n\n\tuseEffect(() => {\n\t\tif (lastRebuiltTs != null) refetch();\n\t}, [lastRebuiltTs, refetch]);\n\n\tif (error && !data) {\n\t\treturn (\n\t\t\t<div className=\"rounded-lg border border-border bg-bg-card p-6 text-sm text-danger\">\n\t\t\t\tError loading evolution graph: {error.message}\n\t\t\t</div>\n\t\t);\n\t}\n\tif (loading && !data) {\n\t\treturn (\n\t\t\t<div className=\"rounded-lg border border-border bg-bg-card p-6 text-sm text-muted-foreground\">\n\t\t\t\tLoading evolution graph…\n\t\t\t</div>\n\t\t);\n\t}\n\n\treturn (\n\t\t<Card>\n\t\t\t<CardContent>\n\t\t\t\t<h3 className=\"mb-2 font-heading text-sm font-semibold text-muted-foreground\">\n\t\t\t\t\tTopic evolution\n\t\t\t\t</h3>\n\t\t\t\t{data ? (\n\t\t\t\t\t<TopicEvolutionGraph data={data} />\n\t\t\t\t) : (\n\t\t\t\t\t<p className=\"text-sm text-muted-foreground\">No data yet.</p>\n\t\t\t\t)}\n\t\t\t</CardContent>\n\t\t</Card>\n\t);\n}\n","/**\n * dashboard-client/src/tabs/WikiTab.tsx — Wiki Revival (W3): wiki landing list.\n *\n * Replaces TopicsTab in the App registry (TopicsTab.tsx kept, unused). Renders\n * the wiki index: each auto-categorized topic with its resolved label (edited\n * badge when user-renamed), member count, curation badges (label/merge/split),\n * and a click-to-open that pushes a client-side page state to WikiPage.\n *\n * Styling is Tailwind + shadcn (Card, Badge, Button) — no legacy CSS classes.\n */\n\nimport type React from \"react\";\nimport { useCallback, useMemo, useState } from \"react\";\nimport { useApi } from \"../hooks/useApi\";\nimport { fetchWikiIndex } from \"../api/client\";\nimport type { WikiIndexEntry, WikiIndexResponse } from \"@contracts\";\nimport { Badge } from \"../components/ui/badge\";\nimport { Button } from \"../components/ui/button\";\nimport { Toggle } from \"../components/ui/toggle\";\nimport WikiPage from \"./WikiTab/WikiPage\";\nimport TopicEvolutionView from \"./WikiTab/TopicEvolutionView\";\n\ntype WikiView = \"list\" | \"evolution\";\n\nconst KIND_BADGES: Record<string, \"default\" | \"accent\" | \"warning\"> = {\n\tlabel: \"accent\",\n\tmerge: \"warning\",\n\tsplit: \"default\",\n};\n\nfunction fmtTs(ms: number | null): string {\n\tif (!ms) return \"—\";\n\treturn new Date(ms).toLocaleString();\n}\n\nfunction ViewToggle({\n\tview,\n\tonChange,\n}: {\n\tview: WikiView;\n\tonChange: (v: WikiView) => void;\n}): React.ReactElement {\n\treturn (\n\t\t<div className=\"flex gap-1\">\n\t\t\t<Toggle pressed={view === \"list\"} onClick={() => onChange(\"list\")}>\n\t\t\t\tTopics\n\t\t\t</Toggle>\n\t\t\t<Toggle\n\t\t\t\tpressed={view === \"evolution\"}\n\t\t\t\tonClick={() => onChange(\"evolution\")}\n\t\t\t>\n\t\t\t\tEvolution\n\t\t\t</Toggle>\n\t\t</div>\n\t);\n}\n\ninterface RowProps {\n\tentry: WikiIndexEntry;\n\tonOpen: (id: string) => void;\n}\n\nfunction TopicRow({ entry, onOpen }: RowProps): React.ReactElement {\n\treturn (\n\t\t<tr className=\"border-b border-border/60 last:border-0 hover:bg-bg-elevated/40\">\n\t\t\t<td className=\"py-2 pr-3\">\n\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t<span className=\"font-medium text-foreground\">{entry.label}</span>\n\t\t\t\t\t{entry.edited && <Badge variant=\"outline\">edited</Badge>}\n\t\t\t\t</div>\n\t\t\t</td>\n\t\t\t<td className=\"py-2 pr-3 text-sm text-muted-foreground\">\n\t\t\t\t{entry.memoryCount}\n\t\t\t</td>\n\t\t\t<td className=\"py-2 pr-3\">\n\t\t\t\t<div className=\"flex flex-wrap gap-1\">\n\t\t\t\t\t{entry.overrideKinds.map((k) => (\n\t\t\t\t\t\t<Badge key={k} variant={KIND_BADGES[k] ?? \"default\"}>\n\t\t\t\t\t\t\t{k}\n\t\t\t\t\t\t</Badge>\n\t\t\t\t\t))}\n\t\t\t\t</div>\n\t\t\t</td>\n\t\t\t<td className=\"py-2 pr-3 text-xs text-muted-foreground\">\n\t\t\t\t{fmtTs(entry.lastUpdated)}\n\t\t\t</td>\n\t\t\t<td className=\"py-2 text-right\">\n\t\t\t\t<Button\n\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\tonClick={() => onOpen(entry.id)}\n\t\t\t\t>\n\t\t\t\t\tOpen\n\t\t\t\t</Button>\n\t\t\t</td>\n\t\t</tr>\n\t);\n}\n\nexport default function WikiTab(): React.ReactElement {\n\tconst { data, loading, error } = useApi<WikiIndexResponse>(\n\t\tuseCallback(() => fetchWikiIndex(), []),\n\t\t{ pollInterval: 30_000 },\n\t);\n\tconst [view, setView] = useState<WikiView>(\"list\");\n\tconst [query, setQuery] = useState(\"\");\n\tconst [openId, setOpenId] = useState<string | null>(null);\n\n\tconst filtered = useMemo(() => {\n\t\tif (!data) return [];\n\t\tconst q = query.trim().toLowerCase();\n\t\tif (!q) return data.topics;\n\t\treturn data.topics.filter((t) =>\n\t\t\tt.label.toLowerCase().includes(q),\n\t\t);\n\t}, [data, query]);\n\n\tif (openId) {\n\t\treturn <WikiPage topicId={openId} onBack={() => setOpenId(null)} />;\n\t}\n\n\tif (view === \"evolution\") {\n\t\treturn (\n\t\t\t<div className=\"flex flex-col gap-3\">\n\t\t\t\t<ViewToggle view={view} onChange={setView} />\n\t\t\t\t<TopicEvolutionView />\n\t\t\t</div>\n\t\t);\n\t}\n\n\tif (error && !data) {\n\t\treturn (\n\t\t\t<div className=\"rounded-lg border border-border bg-bg-card p-6 text-sm text-danger\">\n\t\t\t\tError loading wiki: {error.message}\n\t\t\t</div>\n\t\t);\n\t}\n\tif (loading && !data) {\n\t\treturn (\n\t\t\t<div className=\"rounded-lg border border-border bg-bg-card p-6 text-sm text-muted-foreground\">\n\t\t\t\tLoading wiki…\n\t\t\t</div>\n\t\t);\n\t}\n\tif (!data) {\n\t\treturn (\n\t\t\t<div className=\"rounded-lg border border-border bg-bg-card p-6 text-sm text-muted-foreground\">\n\t\t\t\tNo wiki data available.\n\t\t\t</div>\n\t\t);\n\t}\n\tif (data.totalTopics === 0) {\n\t\treturn (\n\t\t\t<div className=\"rounded-lg border border-border bg-bg-card p-6 text-sm text-muted-foreground\">\n\t\t\t\t<h3 className=\"mb-2 font-heading text-base text-foreground\">Wiki</h3>\n\t\t\t\t<p>\n\t\t\t\t\tNo topics yet. Topics are auto-generated after every Nth compaction\n\t\t\t\t\tfrom real memory embeddings (k-means + TF-IDF).\n\t\t\t\t</p>\n\t\t\t\t<p className=\"mt-2\">Check back after a few more compaction cycles.</p>\n\t\t\t</div>\n\t\t);\n\t}\n\n\treturn (\n\t\t<div className=\"flex flex-col gap-4\">\n\t\t\t<div className=\"flex flex-wrap items-end justify-between gap-2\">\n\t\t\t\t<div className=\"flex items-center gap-3\">\n\t\t\t\t\t<ViewToggle view={view} onChange={setView} />\n\t\t\t\t\t<div>\n\t\t\t\t\t\t<h2 className=\"font-heading text-lg font-semibold\">Wiki</h2>\n\t\t\t\t\t\t<p className=\"text-sm text-muted-foreground\">\n\t\t\t\t\t\t\t{data.totalTopics} topic{data.totalTopics !== 1 ? \"s\" : \"\"} ·{\" \"}\n\t\t\t\t\t\t\t{data.totalMemories} assigned memori\n\t\t\t\t\t\t\t{data.totalMemories !== 1 ? \"es\" : \"y\"}\n\t\t\t\t\t\t\t{data.lastRebuildAt != null && (\n\t\t\t\t\t\t\t\t<> · last rebuild {fmtTs(data.lastRebuildAt)}</>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t<input\n\t\t\t\t\ttype=\"search\"\n\t\t\t\t\tplaceholder=\"Filter topics by label…\"\n\t\t\t\t\taria-label=\"Filter topics by label\"\n\t\t\t\t\tvalue={query}\n\t\t\t\t\tonChange={(e) => setQuery(e.target.value)}\n\t\t\t\t\tclassName=\"w-64 rounded-md border border-border bg-bg-elevated/50 px-3 py-2 text-sm outline-none transition-colors focus:border-primary\"\n\t\t\t\t/>\n\t\t\t</div>\n\n\t\t\t<div className=\"overflow-x-auto rounded-lg border border-border bg-bg-card\">\n\t\t\t\t<table className=\"w-full table-fixed text-sm\">\n\t\t\t\t\t<thead>\n\t\t\t\t\t\t<tr className=\"border-b border-border text-left text-xs uppercase text-muted-foreground\">\n\t\t\t\t\t\t\t<th className=\"w-1/3 px-4 py-2 font-medium\">Label</th>\n\t\t\t\t\t\t\t<th className=\"w-16 py-2 font-medium\">Memories</th>\n\t\t\t\t\t\t\t<th className=\"w-40 py-2 font-medium\">Badges</th>\n\t\t\t\t\t\t\t<th className=\"w-40 py-2 font-medium\">Last updated</th>\n\t\t\t\t\t\t\t<th className=\"w-20 py-2\" />\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t</thead>\n\t\t\t\t\t<tbody>\n\t\t\t\t\t\t{filtered.map((t) => (\n\t\t\t\t\t\t\t<TopicRow key={t.id} entry={t} onOpen={setOpenId} />\n\t\t\t\t\t\t))}\n\t\t\t\t\t\t{filtered.length === 0 && (\n\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t\t<td\n\t\t\t\t\t\t\t\t\tcolSpan={5}\n\t\t\t\t\t\t\t\t\tclassName=\"px-4 py-6 text-center text-muted-foreground\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\tNo topics match \"{query}\".\n\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</tbody>\n\t\t\t\t</table>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n"],"names":["memberSnippet","id","method","base","WikiPageControls","topicId","others","members","onMutated","mode","setMode","useState","label","setLabel","target","setTarget","picked","setPicked","busy","setBusy","error","setError","open","m","submit","renameTopic","mergeTopics","splitTopic","e","jsxs","Fragment","jsx","Button","o","prev","x","W","H","BAR_W","GAP","fmtBucket","ts","TopicTimeline","data","useApi","useCallback","fetchTopicTimeline","buckets","total","max","b","totalWidth","scale","i","h","fmtTs","ms","METHOD_LABELS","WikiPage","onBack","dirty","setDirty","bump","d","page","loading","fetchWikiTopic","index","fetchWikiIndex","labelOf","useMemo","map","t","otherTopics","Badge","Card","CardContent","p","REPULSION","ATTRACTION","CENTER_GRAVITY","DAMPING","SPEED_LIMIT","MIN_VELOCITY","MAX_ITERATIONS","edgeColor","kind","initialLayout","nodes","n","angle","applyForces","sim","edges","fx","fy","j","dx","dy","distSq","dist","other","speed","TopicEvolutionGraph","simRef","useRef","animRef","setFrame","scrub","setScrub","useEffect","indexById","si","ti","timeByActivity","iter","tick","scrubMax","scrubBucket","idx","activeSet","a","active","r","isWikiRebuilt","TopicEvolutionView","refetch","fetchTopicEvolution","events","useSSE","lastRebuiltTs","rebuilt","KIND_BADGES","ViewToggle","view","onChange","Toggle","TopicRow","entry","onOpen","k","WikiTab","setView","query","setQuery","openId","setOpenId","filtered","q"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{r as o,j as s,n as i,a0 as d}from"./index-B93cewiX.js";const l=d("inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/60 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",{variants:{variant:{default:"bg-primary text-primary-foreground hover:bg-primary/90 glow-primary",outline:"border border-border bg-transparent hover:bg-bg-elevated hover:text-foreground",ghost:"hover:bg-bg-elevated hover:text-foreground",glass:"glass-panel electric-hover"},size:{default:"h-9 px-4 py-2",sm:"h-8 rounded-md px-3 text-xs",lg:"h-10 rounded-md px-8",icon:"h-9 w-9"}},defaultVariants:{variant:"default",size:"default"}}),u=o.forwardRef(({className:e,variant:r,size:t,...a},n)=>s.jsx("button",{ref:n,className:i(l({variant:r,size:t,className:e})),...a}));u.displayName="Button";export{u as B};
|
|
2
|
+
//# sourceMappingURL=button-DtEsZTUE.js.map
|
package/extensions/dashboard-client/dist/assets/{button-YcV2VQEP.js.map → button-DtEsZTUE.js.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"button-
|
|
1
|
+
{"version":3,"file":"button-DtEsZTUE.js","sources":["../../src/components/ui/button.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { cn } from \"../../utils/cn\";\n\nconst buttonVariants = cva(\n \"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/60 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0\",\n {\n variants: {\n variant: {\n default: \"bg-primary text-primary-foreground hover:bg-primary/90 glow-primary\",\n outline: \"border border-border bg-transparent hover:bg-bg-elevated hover:text-foreground\",\n ghost: \"hover:bg-bg-elevated hover:text-foreground\",\n glass: \"glass-panel electric-hover\",\n },\n size: {\n default: \"h-9 px-4 py-2\",\n sm: \"h-8 rounded-md px-3 text-xs\",\n lg: \"h-10 rounded-md px-8\",\n icon: \"h-9 w-9\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n },\n);\n\nexport interface ButtonProps\n extends React.ButtonHTMLAttributes<HTMLButtonElement>,\n VariantProps<typeof buttonVariants> {}\n\nconst Button = React.forwardRef<HTMLButtonElement, ButtonProps>(\n ({ className, variant, size, ...props }, ref) => (\n <button\n ref={ref}\n className={cn(buttonVariants({ variant, size, className }))}\n {...props}\n />\n ),\n);\nButton.displayName = \"Button\";\n\nexport { Button, buttonVariants };\n"],"names":["buttonVariants","cva","Button","React.forwardRef","className","variant","size","props","ref","jsx","cn"],"mappings":"8DAIA,MAAMA,EAAiBC,EACrB,2SACA,CACE,SAAU,CACR,QAAS,CACP,QAAS,sEACT,QAAS,iFACT,MAAO,6CACP,MAAO,4BAAA,EAET,KAAM,CACJ,QAAS,gBACT,GAAI,8BACJ,GAAI,uBACJ,KAAM,SAAA,CACR,EAEF,gBAAiB,CACf,QAAS,UACT,KAAM,SAAA,CACR,CAEJ,EAMMC,EAASC,EAAAA,WACb,CAAC,CAAE,UAAAC,EAAW,QAAAC,EAAS,KAAAC,EAAM,GAAGC,CAAA,EAASC,IACvCC,EAAAA,IAAC,SAAA,CACC,IAAAD,EACA,UAAWE,EAAGV,EAAe,CAAE,QAAAK,EAAS,KAAAC,EAAM,UAAAF,CAAA,CAAW,CAAC,EACzD,GAAGG,CAAA,CAAA,CAGV,EACAL,EAAO,YAAc"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{r,j as t,n as d}from"./index-
|
|
2
|
-
//# sourceMappingURL=card-
|
|
1
|
+
import{r,j as t,n as d}from"./index-B93cewiX.js";const o=r.forwardRef(({className:a,...s},e)=>t.jsx("div",{ref:e,className:d("glass-panel p-4",a),...s}));o.displayName="Card";const n=r.forwardRef(({className:a,...s},e)=>t.jsx("div",{ref:e,className:d("mb-3 flex flex-col space-y-1.5",a),...s}));n.displayName="CardHeader";const l=r.forwardRef(({className:a,...s},e)=>t.jsx("h3",{ref:e,className:d("font-heading text-sm font-semibold leading-none tracking-tight",a),...s}));l.displayName="CardTitle";const i=r.forwardRef(({className:a,...s},e)=>t.jsx("div",{ref:e,className:d("text-sm",a),...s}));i.displayName="CardContent";export{o as C,n as a,l as b,i as c};
|
|
2
|
+
//# sourceMappingURL=card-DcfTmw-4.js.map
|
package/extensions/dashboard-client/dist/assets/{card-Bwxzo50p.js.map → card-DcfTmw-4.js.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"card-
|
|
1
|
+
{"version":3,"file":"card-DcfTmw-4.js","sources":["../../src/components/ui/card.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\nconst Card = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n ({ className, ...props }, ref) => (\n <div ref={ref} className={cn(\"glass-panel p-4\", className)} {...props} />\n ),\n);\nCard.displayName = \"Card\";\n\nconst CardHeader = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n ({ className, ...props }, ref) => (\n <div ref={ref} className={cn(\"mb-3 flex flex-col space-y-1.5\", className)} {...props} />\n ),\n);\nCardHeader.displayName = \"CardHeader\";\n\nconst CardTitle = React.forwardRef<HTMLHeadingElement, React.HTMLAttributes<HTMLHeadingElement>>(\n ({ className, ...props }, ref) => (\n <h3 ref={ref} className={cn(\"font-heading text-sm font-semibold leading-none tracking-tight\", className)} {...props} />\n ),\n);\nCardTitle.displayName = \"CardTitle\";\n\nconst CardContent = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n ({ className, ...props }, ref) => (\n <div ref={ref} className={cn(\"text-sm\", className)} {...props} />\n ),\n);\nCardContent.displayName = \"CardContent\";\n\nexport { Card, CardHeader, CardTitle, CardContent };\n"],"names":["Card","React.forwardRef","className","props","ref","jsx","cn","CardHeader","CardTitle","CardContent"],"mappings":"iDAGA,MAAMA,EAAOC,EAAAA,WACX,CAAC,CAAE,UAAAC,EAAW,GAAGC,CAAA,EAASC,IACxBC,EAAAA,IAAC,MAAA,CAAI,IAAAD,EAAU,UAAWE,EAAG,kBAAmBJ,CAAS,EAAI,GAAGC,CAAA,CAAO,CAE3E,EACAH,EAAK,YAAc,OAEnB,MAAMO,EAAaN,EAAAA,WACjB,CAAC,CAAE,UAAAC,EAAW,GAAGC,CAAA,EAASC,IACxBC,EAAAA,IAAC,MAAA,CAAI,IAAAD,EAAU,UAAWE,EAAG,iCAAkCJ,CAAS,EAAI,GAAGC,CAAA,CAAO,CAE1F,EACAI,EAAW,YAAc,aAEzB,MAAMC,EAAYP,EAAAA,WAChB,CAAC,CAAE,UAAAC,EAAW,GAAGC,CAAA,EAASC,IACxBC,EAAAA,IAAC,KAAA,CAAG,IAAAD,EAAU,UAAWE,EAAG,iEAAkEJ,CAAS,EAAI,GAAGC,CAAA,CAAO,CAEzH,EACAK,EAAU,YAAc,YAExB,MAAMC,EAAcR,EAAAA,WAClB,CAAC,CAAE,UAAAC,EAAW,GAAGC,CAAA,EAASC,IACxBC,EAAAA,IAAC,MAAA,CAAI,IAAAD,EAAU,UAAWE,EAAG,UAAWJ,CAAS,EAAI,GAAGC,CAAA,CAAO,CAEnE,EACAM,EAAY,YAAc"}
|