pi-mega-compact 0.11.6 → 0.11.8
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/routes-memory-map.js +8 -15
- package/dist/extensions/mega-commands.js +65 -5
- package/dist/extensions/mega-config.js +6 -0
- package/dist/extensions/mega-events/context-handler.js +65 -0
- package/dist/src/config/turns.js +2 -1
- package/dist/src/memoryGraph/embedding.js +50 -0
- package/dist/src/memoryGraph/gates.js +379 -0
- package/dist/src/memoryGraph/sources.js +390 -0
- package/dist/src/memoryGraph.js +133 -259
- package/dist/src/recall.js +136 -1
- package/dist/src/store/sqlite/embedding-cache.js +71 -0
- package/dist/src/store/sqlite.js +1 -0
- package/dist/src/topics/cluster.js +10 -2
- package/extensions/dashboard-client/dist/assets/{AchievementTiles-DR9cLGDL.js → AchievementTiles-BESF1HT1.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{AchievementTiles-DR9cLGDL.js.map → AchievementTiles-BESF1HT1.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{AchievementsTab-svNUgJ1e.js → AchievementsTab-8RzNnz8u.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{AchievementsTab-svNUgJ1e.js.map → AchievementsTab-8RzNnz8u.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{CacheTab-ASwhOMjR.js → CacheTab-CUIiUgh3.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{CacheTab-ASwhOMjR.js.map → CacheTab-CUIiUgh3.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{ConfigTab-CmmWheuW.js → ConfigTab-D85fB9HP.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{ConfigTab-CmmWheuW.js.map → ConfigTab-D85fB9HP.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{EventsTab-CFE7jLsa.js → EventsTab-TCLR81G5.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{EventsTab-CFE7jLsa.js.map → EventsTab-TCLR81G5.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{GameTab-CJAX_th_.js → GameTab-BqneNRYH.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{GameTab-CJAX_th_.js.map → GameTab-BqneNRYH.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{MaintenanceTab-P4SUsvMf.js → MaintenanceTab-LblMg3NB.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MaintenanceTab-P4SUsvMf.js.map → MaintenanceTab-LblMg3NB.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/MemoryMapTab-CpjMQwRr.js +2 -0
- package/extensions/dashboard-client/dist/assets/MemoryMapTab-CpjMQwRr.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/{MetricsTab-CHQscixy.js → MetricsTab-LxNtNzLQ.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MetricsTab-CHQscixy.js.map → MetricsTab-LxNtNzLQ.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{OverviewTab-DBs4DJdd.js → OverviewTab-BXKUZbT6.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{OverviewTab-DBs4DJdd.js.map → OverviewTab-BXKUZbT6.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{ReposTab-DeFz76L5.js → ReposTab-DTooKeAi.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{ReposTab-DeFz76L5.js.map → ReposTab-DTooKeAi.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{SessionsTab-BWNsj1OB.js → SessionsTab-Devulgm9.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{SessionsTab-BWNsj1OB.js.map → SessionsTab-Devulgm9.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{SetupTab-uS6A0xmw.js → SetupTab-By9eqUoE.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{SetupTab-uS6A0xmw.js.map → SetupTab-By9eqUoE.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{TimeSavedCard-CiqCOQuL.js → TimeSavedCard-z4M8TDLm.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{TimeSavedCard-CiqCOQuL.js.map → TimeSavedCard-z4M8TDLm.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/TopicsTab-bCGZSfLN.js +2 -0
- package/extensions/dashboard-client/dist/assets/TopicsTab-bCGZSfLN.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/{TurnsTab-CVz4f4-o.js → TurnsTab-m_XzE0H4.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{TurnsTab-CVz4f4-o.js.map → TurnsTab-m_XzE0H4.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{index-DrktNOoT.js → index-PRkilPXw.js} +3 -3
- package/extensions/dashboard-client/dist/assets/{index-DrktNOoT.js.map → index-PRkilPXw.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{useSSE-Bf5LOLSi.js → useSSE-CGzgc2IK.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{useSSE-Bf5LOLSi.js.map → useSSE-CGzgc2IK.js.map} +1 -1
- package/extensions/dashboard-client/dist/index.html +1 -1
- package/extensions/dashboard-client/src/memory-map-layout.ts +161 -0
- package/extensions/dashboard-client/src/memory-map-shapes.tsx +126 -0
- package/extensions/dashboard-client/src/tabs/MemoryMapTab.tsx +201 -165
- package/extensions/dashboard-client/src/tabs/ReposTab.tsx +21 -0
- package/extensions/dashboard-client/src/tabs/TopicsTab.tsx +1 -1
- package/extensions/dashboard-server/api-contracts/index.ts +2 -1
- package/extensions/dashboard-server/api-contracts/memory-map.ts +14 -6
- package/extensions/dashboard-server/routes-memory-map.ts +21 -29
- package/extensions/mega-commands.ts +63 -6
- package/extensions/mega-config.ts +18 -0
- package/extensions/mega-events/context-handler.ts +80 -0
- package/package.json +1 -1
- package/src/config/turns.ts +5 -2
- package/src/memoryGraph/embedding.ts +63 -0
- package/src/memoryGraph/gates.ts +451 -0
- package/src/memoryGraph/sources.ts +484 -0
- package/src/memoryGraph.ts +188 -350
- package/src/recall.ts +168 -4
- package/src/store/sqlite/embedding-cache.ts +81 -0
- package/src/store/sqlite.ts +1 -0
- package/src/topics/cluster.ts +11 -1
- package/extensions/dashboard-client/dist/assets/MemoryMapTab-BBiH76eF.js +0 -2
- package/extensions/dashboard-client/dist/assets/MemoryMapTab-BBiH76eF.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/TopicsTab-BWWQxltv.js +0 -2
- package/extensions/dashboard-client/dist/assets/TopicsTab-BWWQxltv.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EventsTab-CFE7jLsa.js","sources":["../../src/components/EventStream.tsx","../../src/components/EventCategoryFilter.tsx","../../src/tabs/EventsTab.tsx"],"sourcesContent":["/**\n * dashboard-client/src/components/EventStream.tsx — SSE event list with filters.\n *\n * Type badge colored by event type. Timestamp + expandable detail row.\n * Type filter chips (all + per-type). Virtualization: manual windowing for\n * 500+ events (render last N visible).\n */\n\nimport type React from \"react\";\nimport { useState, useMemo } from \"react\";\nimport type { SseEvent } from \"@contracts\";\n\nexport interface EventStreamProps {\n\tevents: SseEvent[];\n}\n\nconst TYPE_COLORS: Record<string, string> = {\n\tcompact_start: \"ev-compact\",\n\tcompact_end: \"ev-compact\",\n\tcompact_trigger: \"ev-trigger\",\n\tcompact_skip: \"ev-trigger\",\n\ttier_changed: \"ev-tier\",\n\tmodel_changed: \"ev-tier\",\n\tpressure_lifted: \"ev-tier\",\n\tcheckpoint_persisted: \"ev-checkpoint\",\n\trecall_inject: \"ev-recall\",\n\tanchors_updated: \"ev-recall\",\n\tconfig_updated: \"ev-config\",\n\tconfig_preset: \"ev-config\",\n\tcrew_presence_changed: \"ev-crew\",\n\tcrew_turn_changed: \"ev-crew\",\n\tcrew_bandit_chosen: \"ev-crew\",\n\tsession_sample: \"ev-recall\",\n\tgame_ritual_start: \"ev-game\",\n\tgame_ritual_stage: \"ev-game\",\n\tgame_ritual_end: \"ev-game\",\n\tgame_mode_changed: \"ev-game\",\n\tgame_render: \"ev-game\",\n};\n\n// Filterable event types (spec: compact_start/end, recall_inject, checkpoint_persisted + all).\nconst FILTER_TYPES = [\n\t\"all\",\n\t\"compact_start\",\n\t\"compact_end\",\n\t\"recall_inject\",\n\t\"checkpoint_persisted\",\n] as const;\ntype FilterType = (typeof FILTER_TYPES)[number];\n\nconst RENDER_WINDOW = 200; // render last 200 to keep DOM light under 500+ events\n\nfunction summarize(ev: SseEvent): string {\n\tswitch (ev.type) {\n\t\tcase \"compact_start\":\n\t\t\treturn `trigger=${ev.trigger} session=${ev.sessionId.slice(0, 8)}`;\n\t\tcase \"compact_end\":\n\t\t\treturn `freed=${ev.tokensFreed} ok=${ev.success} cp=${ev.checkpointId.slice(0, 8)}`;\n\t\tcase \"compact_trigger\":\n\t\t\treturn `pressure=${ev.pressure}% threshold=${ev.threshold}% armed=${ev.armed}`;\n\t\tcase \"compact_skip\":\n\t\t\treturn `reason=${ev.reason}`;\n\t\tcase \"tier_changed\":\n\t\t\treturn `${ev.from} → ${ev.to} ctx=${ev.contextPct}%`;\n\t\tcase \"model_changed\":\n\t\t\treturn `${ev.providerName}/${ev.model}`;\n\t\tcase \"pressure_lifted\":\n\t\t\treturn `${ev.beforePct}% → ${ev.afterPct}%`;\n\t\tcase \"checkpoint_persisted\":\n\t\t\treturn `cp=${ev.checkpointId.slice(0, 8)} sessionTok=${ev.sessionTokens}`;\n\t\tcase \"recall_inject\":\n\t\t\treturn `q=\"${ev.query.slice(0, 40)}\" chunks=${ev.chunks} tok=${ev.tokens}`;\n\t\tcase \"anchors_updated\":\n\t\t\treturn `count=${ev.count} pinned=${ev.pinned}`;\n\t\tcase \"config_updated\":\n\t\t\treturn `key=${ev.key}`;\n\t\tcase \"config_preset\":\n\t\t\treturn `preset=${ev.preset}`;\n\t\tcase \"crew_presence_changed\":\n\t\t\treturn `agents=${ev.activeAgents} turn=${ev.currentTurn}`;\n\t\tcase \"crew_turn_changed\":\n\t\t\treturn `turn=${ev.turnIndex} agent=${ev.agentName}`;\n\t\tcase \"crew_bandit_chosen\":\n\t\t\treturn `agent=${ev.chosenAgent} score=${ev.score} regret=${ev.regret}`;\n\t\tcase \"session_sample\":\n\t\t\treturn `sess=${ev.sessionId.slice(0, 8)} tok=${ev.tokens.toLocaleString()} ctx=${Math.round(ev.percent)}%`;\n\t\tcase \"game_ritual_start\":\n\t\tcase \"game_ritual_stage\":\n\t\tcase \"game_ritual_end\":\n\t\tcase \"game_mode_changed\":\n\t\tcase \"game_render\":\n\t\t\treturn `stage=${ev.type}`;\n\t}\n}\n\nfunction formatTs(ts: string): string {\n\ttry {\n\t\tconst d = new Date(ts);\n\t\treturn d.toLocaleTimeString();\n\t} catch {\n\t\treturn ts;\n\t}\n}\n\nexport function EventStream({ events }: EventStreamProps): React.ReactElement {\n\tconst [filter, setFilter] = useState<FilterType>(\"all\");\n\tconst [expanded, setExpanded] = useState<number | null>(null);\n\n\tconst filtered = useMemo(() => {\n\t\tconst list =\n\t\t\tfilter === \"all\" ? events : events.filter((e) => e.type === filter);\n\t\t// Newest first; render only the last RENDER_WINDOW to keep DOM light.\n\t\treturn list.slice(-RENDER_WINDOW).reverse();\n\t}, [events, filter]);\n\n\treturn (\n\t\t<div className=\"event-stream\">\n\t\t\t<div className=\"event-filters\" role=\"toolbar\">\n\t\t\t\t{FILTER_TYPES.map((t) => (\n\t\t\t\t\t<button\n\t\t\t\t\t\tkey={t}\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tclassName={`filter-chip ${filter === t ? \"active\" : \"\"}`}\n\t\t\t\t\t\tonClick={() => setFilter(t)}\n\t\t\t\t\t>\n\t\t\t\t\t\t{t === \"all\" ? \"all\" : t.replace(/_/g, \" \")}\n\t\t\t\t\t</button>\n\t\t\t\t))}\n\t\t\t\t<span className=\"event-count\">{events.length} buffered</span>\n\t\t\t</div>\n\t\t\t<ul className=\"event-list\">\n\t\t\t\t{filtered.length === 0 && (\n\t\t\t\t\t<li className=\"event-empty\">No events yet.</li>\n\t\t\t\t)}\n\t\t\t\t{filtered.map((ev, idx) => {\n\t\t\t\t\tconst cls = TYPE_COLORS[ev.type] ?? \"ev-default\";\n\t\t\t\t\treturn (\n\t\t\t\t\t\t<li\n\t\t\t\t\t\t\tkey={`${ev.ts}-${idx}`}\n\t\t\t\t\t\t\tclassName={`event-row ${cls}`}\n\t\t\t\t\t\t\tonClick={() => setExpanded(expanded === idx ? null : idx)}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<span className=\"ev-time\">{formatTs(ev.ts)}</span>\n\t\t\t\t\t\t\t<span className=\"ev-type\">{ev.type}</span>\n\t\t\t\t\t\t\t<span className=\"ev-summary\">{summarize(ev)}</span>\n\t\t\t\t\t\t\t{expanded === idx && (\n\t\t\t\t\t\t\t\t<pre className=\"ev-detail\">{JSON.stringify(ev, null, 2)}</pre>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</li>\n\t\t\t\t\t);\n\t\t\t\t})}\n\t\t\t</ul>\n\t\t</div>\n\t);\n}\n","/**\n * dashboard-client/src/components/EventCategoryFilter.tsx\n *\n * Category-level filter chips for the Events tab. Filters SSE events by\n * category: all / compact / recall / config / crew / game.\n */\n\nimport type React from \"react\";\n\nexport type EventCategory =\n\t| \"all\"\n\t| \"compact\"\n\t| \"recall\"\n\t| \"config\"\n\t| \"crew\"\n\t| \"game\";\n\nconst CATEGORIES: readonly EventCategory[] = [\n\t\"all\",\n\t\"compact\",\n\t\"recall\",\n\t\"config\",\n\t\"crew\",\n\t\"game\",\n] as const;\n\nexport interface EventCategoryFilterProps {\n\t/** Currently active category. */\n\tactive: EventCategory;\n\t/** Called when the user selects a category. */\n\tonFilter: (category: EventCategory) => void;\n}\n\nexport function EventCategoryFilter({\n\tactive,\n\tonFilter,\n}: EventCategoryFilterProps): React.ReactElement {\n\treturn (\n\t\t<div className=\"event-category-filter\" role=\"toolbar\" aria-label=\"Event category filter\">\n\t\t\t{CATEGORIES.map((cat) => (\n\t\t\t\t<button\n\t\t\t\t\tkey={cat}\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tclassName={`category-chip${active === cat ? \" active\" : \"\"}`}\n\t\t\t\t\tonClick={() => onFilter(cat)}\n\t\t\t\t\taria-pressed={active === cat}\n\t\t\t\t>\n\t\t\t\t\t{cat}\n\t\t\t\t</button>\n\t\t\t))}\n\t\t</div>\n\t);\n}\n","/**\n * dashboard-client/src/tabs/EventsTab.tsx — Events tab (C1).\n *\n * Live SSE event stream via useSSE + EventStream with category filter chips.\n * Shows connection status + event count. Category filter uses the existing\n * EventCategoryFilter component to filter by all/compact/recall/config/crew/game.\n */\n\nimport type React from \"react\";\nimport { useState, useMemo } from \"react\";\nimport type { SseEvent } from \"@contracts\";\nimport { useSSE } from \"../hooks/useSSE\";\nimport { EventStream } from \"../components/EventStream\";\nimport {\n\tEventCategoryFilter,\n\ttype EventCategory,\n} from \"../components/EventCategoryFilter\";\n\nconst COMPACT_TYPES = new Set([\n\t\"compact_start\",\n\t\"compact_end\",\n\t\"compact_trigger\",\n\t\"compact_skip\",\n]);\nconst CONFIG_TYPES = new Set([\n\t\"config_updated\",\n\t\"config_preset\",\n\t\"tier_changed\",\n\t\"model_changed\",\n\t\"pressure_lifted\",\n\t\"anchors_updated\",\n\t\"checkpoint_persisted\",\n]);\nconst CREW_TYPES = new Set([\n\t\"crew_presence_changed\",\n\t\"crew_turn_changed\",\n\t\"crew_bandit_chosen\",\n]);\nconst GAME_TYPES = new Set([\n\t\"game_ritual_start\",\n\t\"game_ritual_stage\",\n\t\"game_ritual_end\",\n\t\"game_mode_changed\",\n\t\"game_render\",\n]);\n\nfunction matchesCategory(ev: SseEvent, cat: EventCategory): boolean {\n\tif (cat === \"all\") return true;\n\tconst t = ev.type;\n\tif (cat === \"compact\") return COMPACT_TYPES.has(t);\n\tif (cat === \"recall\") return t === \"recall_inject\";\n\tif (cat === \"config\") return CONFIG_TYPES.has(t);\n\tif (cat === \"crew\") return CREW_TYPES.has(t);\n\treturn GAME_TYPES.has(t); // cat === \"game\"\n}\n\nexport default function EventsTab(): React.ReactElement {\n\tconst { events, status, eventCount } = useSSE();\n\tconst [category, setCategory] = useState<EventCategory>(\"all\");\n\n\tconst filtered = useMemo(\n\t\t() => events.filter((e) => matchesCategory(e, category)),\n\t\t[events, category],\n\t);\n\n\tconst statusCls = `sse-status sse-${status}`;\n\tconst statusLabel =\n\t\tstatus === \"connected\"\n\t\t\t? \"connected\"\n\t\t\t: status === \"connecting\"\n\t\t\t\t? \"connecting…\"\n\t\t\t\t: status === \"error\"\n\t\t\t\t\t? \"error\"\n\t\t\t\t\t: \"disconnected\";\n\n\treturn (\n\t\t<div className=\"events-tab\">\n\t\t\t<div className=\"events-header\">\n\t\t\t\t<span className={statusCls} role=\"status\">\n\t\t\t\t\t<span className=\"sse-dot\" aria-hidden=\"true\" />\n\t\t\t\t\t{statusLabel}\n\t\t\t\t</span>\n\t\t\t\t<span className=\"event-count\">{eventCount} events received</span>\n\t\t\t</div>\n\t\t\t<EventCategoryFilter active={category} onFilter={setCategory} />\n\t\t\t<EventStream events={filtered} />\n\t\t</div>\n\t);\n}\n"],"names":["TYPE_COLORS","FILTER_TYPES","summarize","ev","formatTs","ts","EventStream","events","filter","setFilter","useState","expanded","setExpanded","filtered","useMemo","e","jsxs","t","jsx","idx","cls","CATEGORIES","EventCategoryFilter","active","onFilter","cat","COMPACT_TYPES","CONFIG_TYPES","CREW_TYPES","GAME_TYPES","matchesCategory","EventsTab","status","eventCount","useSSE","category","setCategory","statusCls","statusLabel"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"EventsTab-TCLR81G5.js","sources":["../../src/components/EventStream.tsx","../../src/components/EventCategoryFilter.tsx","../../src/tabs/EventsTab.tsx"],"sourcesContent":["/**\n * dashboard-client/src/components/EventStream.tsx — SSE event list with filters.\n *\n * Type badge colored by event type. Timestamp + expandable detail row.\n * Type filter chips (all + per-type). Virtualization: manual windowing for\n * 500+ events (render last N visible).\n */\n\nimport type React from \"react\";\nimport { useState, useMemo } from \"react\";\nimport type { SseEvent } from \"@contracts\";\n\nexport interface EventStreamProps {\n\tevents: SseEvent[];\n}\n\nconst TYPE_COLORS: Record<string, string> = {\n\tcompact_start: \"ev-compact\",\n\tcompact_end: \"ev-compact\",\n\tcompact_trigger: \"ev-trigger\",\n\tcompact_skip: \"ev-trigger\",\n\ttier_changed: \"ev-tier\",\n\tmodel_changed: \"ev-tier\",\n\tpressure_lifted: \"ev-tier\",\n\tcheckpoint_persisted: \"ev-checkpoint\",\n\trecall_inject: \"ev-recall\",\n\tanchors_updated: \"ev-recall\",\n\tconfig_updated: \"ev-config\",\n\tconfig_preset: \"ev-config\",\n\tcrew_presence_changed: \"ev-crew\",\n\tcrew_turn_changed: \"ev-crew\",\n\tcrew_bandit_chosen: \"ev-crew\",\n\tsession_sample: \"ev-recall\",\n\tgame_ritual_start: \"ev-game\",\n\tgame_ritual_stage: \"ev-game\",\n\tgame_ritual_end: \"ev-game\",\n\tgame_mode_changed: \"ev-game\",\n\tgame_render: \"ev-game\",\n};\n\n// Filterable event types (spec: compact_start/end, recall_inject, checkpoint_persisted + all).\nconst FILTER_TYPES = [\n\t\"all\",\n\t\"compact_start\",\n\t\"compact_end\",\n\t\"recall_inject\",\n\t\"checkpoint_persisted\",\n] as const;\ntype FilterType = (typeof FILTER_TYPES)[number];\n\nconst RENDER_WINDOW = 200; // render last 200 to keep DOM light under 500+ events\n\nfunction summarize(ev: SseEvent): string {\n\tswitch (ev.type) {\n\t\tcase \"compact_start\":\n\t\t\treturn `trigger=${ev.trigger} session=${ev.sessionId.slice(0, 8)}`;\n\t\tcase \"compact_end\":\n\t\t\treturn `freed=${ev.tokensFreed} ok=${ev.success} cp=${ev.checkpointId.slice(0, 8)}`;\n\t\tcase \"compact_trigger\":\n\t\t\treturn `pressure=${ev.pressure}% threshold=${ev.threshold}% armed=${ev.armed}`;\n\t\tcase \"compact_skip\":\n\t\t\treturn `reason=${ev.reason}`;\n\t\tcase \"tier_changed\":\n\t\t\treturn `${ev.from} → ${ev.to} ctx=${ev.contextPct}%`;\n\t\tcase \"model_changed\":\n\t\t\treturn `${ev.providerName}/${ev.model}`;\n\t\tcase \"pressure_lifted\":\n\t\t\treturn `${ev.beforePct}% → ${ev.afterPct}%`;\n\t\tcase \"checkpoint_persisted\":\n\t\t\treturn `cp=${ev.checkpointId.slice(0, 8)} sessionTok=${ev.sessionTokens}`;\n\t\tcase \"recall_inject\":\n\t\t\treturn `q=\"${ev.query.slice(0, 40)}\" chunks=${ev.chunks} tok=${ev.tokens}`;\n\t\tcase \"anchors_updated\":\n\t\t\treturn `count=${ev.count} pinned=${ev.pinned}`;\n\t\tcase \"config_updated\":\n\t\t\treturn `key=${ev.key}`;\n\t\tcase \"config_preset\":\n\t\t\treturn `preset=${ev.preset}`;\n\t\tcase \"crew_presence_changed\":\n\t\t\treturn `agents=${ev.activeAgents} turn=${ev.currentTurn}`;\n\t\tcase \"crew_turn_changed\":\n\t\t\treturn `turn=${ev.turnIndex} agent=${ev.agentName}`;\n\t\tcase \"crew_bandit_chosen\":\n\t\t\treturn `agent=${ev.chosenAgent} score=${ev.score} regret=${ev.regret}`;\n\t\tcase \"session_sample\":\n\t\t\treturn `sess=${ev.sessionId.slice(0, 8)} tok=${ev.tokens.toLocaleString()} ctx=${Math.round(ev.percent)}%`;\n\t\tcase \"game_ritual_start\":\n\t\tcase \"game_ritual_stage\":\n\t\tcase \"game_ritual_end\":\n\t\tcase \"game_mode_changed\":\n\t\tcase \"game_render\":\n\t\t\treturn `stage=${ev.type}`;\n\t}\n}\n\nfunction formatTs(ts: string): string {\n\ttry {\n\t\tconst d = new Date(ts);\n\t\treturn d.toLocaleTimeString();\n\t} catch {\n\t\treturn ts;\n\t}\n}\n\nexport function EventStream({ events }: EventStreamProps): React.ReactElement {\n\tconst [filter, setFilter] = useState<FilterType>(\"all\");\n\tconst [expanded, setExpanded] = useState<number | null>(null);\n\n\tconst filtered = useMemo(() => {\n\t\tconst list =\n\t\t\tfilter === \"all\" ? events : events.filter((e) => e.type === filter);\n\t\t// Newest first; render only the last RENDER_WINDOW to keep DOM light.\n\t\treturn list.slice(-RENDER_WINDOW).reverse();\n\t}, [events, filter]);\n\n\treturn (\n\t\t<div className=\"event-stream\">\n\t\t\t<div className=\"event-filters\" role=\"toolbar\">\n\t\t\t\t{FILTER_TYPES.map((t) => (\n\t\t\t\t\t<button\n\t\t\t\t\t\tkey={t}\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tclassName={`filter-chip ${filter === t ? \"active\" : \"\"}`}\n\t\t\t\t\t\tonClick={() => setFilter(t)}\n\t\t\t\t\t>\n\t\t\t\t\t\t{t === \"all\" ? \"all\" : t.replace(/_/g, \" \")}\n\t\t\t\t\t</button>\n\t\t\t\t))}\n\t\t\t\t<span className=\"event-count\">{events.length} buffered</span>\n\t\t\t</div>\n\t\t\t<ul className=\"event-list\">\n\t\t\t\t{filtered.length === 0 && (\n\t\t\t\t\t<li className=\"event-empty\">No events yet.</li>\n\t\t\t\t)}\n\t\t\t\t{filtered.map((ev, idx) => {\n\t\t\t\t\tconst cls = TYPE_COLORS[ev.type] ?? \"ev-default\";\n\t\t\t\t\treturn (\n\t\t\t\t\t\t<li\n\t\t\t\t\t\t\tkey={`${ev.ts}-${idx}`}\n\t\t\t\t\t\t\tclassName={`event-row ${cls}`}\n\t\t\t\t\t\t\tonClick={() => setExpanded(expanded === idx ? null : idx)}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<span className=\"ev-time\">{formatTs(ev.ts)}</span>\n\t\t\t\t\t\t\t<span className=\"ev-type\">{ev.type}</span>\n\t\t\t\t\t\t\t<span className=\"ev-summary\">{summarize(ev)}</span>\n\t\t\t\t\t\t\t{expanded === idx && (\n\t\t\t\t\t\t\t\t<pre className=\"ev-detail\">{JSON.stringify(ev, null, 2)}</pre>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</li>\n\t\t\t\t\t);\n\t\t\t\t})}\n\t\t\t</ul>\n\t\t</div>\n\t);\n}\n","/**\n * dashboard-client/src/components/EventCategoryFilter.tsx\n *\n * Category-level filter chips for the Events tab. Filters SSE events by\n * category: all / compact / recall / config / crew / game.\n */\n\nimport type React from \"react\";\n\nexport type EventCategory =\n\t| \"all\"\n\t| \"compact\"\n\t| \"recall\"\n\t| \"config\"\n\t| \"crew\"\n\t| \"game\";\n\nconst CATEGORIES: readonly EventCategory[] = [\n\t\"all\",\n\t\"compact\",\n\t\"recall\",\n\t\"config\",\n\t\"crew\",\n\t\"game\",\n] as const;\n\nexport interface EventCategoryFilterProps {\n\t/** Currently active category. */\n\tactive: EventCategory;\n\t/** Called when the user selects a category. */\n\tonFilter: (category: EventCategory) => void;\n}\n\nexport function EventCategoryFilter({\n\tactive,\n\tonFilter,\n}: EventCategoryFilterProps): React.ReactElement {\n\treturn (\n\t\t<div className=\"event-category-filter\" role=\"toolbar\" aria-label=\"Event category filter\">\n\t\t\t{CATEGORIES.map((cat) => (\n\t\t\t\t<button\n\t\t\t\t\tkey={cat}\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tclassName={`category-chip${active === cat ? \" active\" : \"\"}`}\n\t\t\t\t\tonClick={() => onFilter(cat)}\n\t\t\t\t\taria-pressed={active === cat}\n\t\t\t\t>\n\t\t\t\t\t{cat}\n\t\t\t\t</button>\n\t\t\t))}\n\t\t</div>\n\t);\n}\n","/**\n * dashboard-client/src/tabs/EventsTab.tsx — Events tab (C1).\n *\n * Live SSE event stream via useSSE + EventStream with category filter chips.\n * Shows connection status + event count. Category filter uses the existing\n * EventCategoryFilter component to filter by all/compact/recall/config/crew/game.\n */\n\nimport type React from \"react\";\nimport { useState, useMemo } from \"react\";\nimport type { SseEvent } from \"@contracts\";\nimport { useSSE } from \"../hooks/useSSE\";\nimport { EventStream } from \"../components/EventStream\";\nimport {\n\tEventCategoryFilter,\n\ttype EventCategory,\n} from \"../components/EventCategoryFilter\";\n\nconst COMPACT_TYPES = new Set([\n\t\"compact_start\",\n\t\"compact_end\",\n\t\"compact_trigger\",\n\t\"compact_skip\",\n]);\nconst CONFIG_TYPES = new Set([\n\t\"config_updated\",\n\t\"config_preset\",\n\t\"tier_changed\",\n\t\"model_changed\",\n\t\"pressure_lifted\",\n\t\"anchors_updated\",\n\t\"checkpoint_persisted\",\n]);\nconst CREW_TYPES = new Set([\n\t\"crew_presence_changed\",\n\t\"crew_turn_changed\",\n\t\"crew_bandit_chosen\",\n]);\nconst GAME_TYPES = new Set([\n\t\"game_ritual_start\",\n\t\"game_ritual_stage\",\n\t\"game_ritual_end\",\n\t\"game_mode_changed\",\n\t\"game_render\",\n]);\n\nfunction matchesCategory(ev: SseEvent, cat: EventCategory): boolean {\n\tif (cat === \"all\") return true;\n\tconst t = ev.type;\n\tif (cat === \"compact\") return COMPACT_TYPES.has(t);\n\tif (cat === \"recall\") return t === \"recall_inject\";\n\tif (cat === \"config\") return CONFIG_TYPES.has(t);\n\tif (cat === \"crew\") return CREW_TYPES.has(t);\n\treturn GAME_TYPES.has(t); // cat === \"game\"\n}\n\nexport default function EventsTab(): React.ReactElement {\n\tconst { events, status, eventCount } = useSSE();\n\tconst [category, setCategory] = useState<EventCategory>(\"all\");\n\n\tconst filtered = useMemo(\n\t\t() => events.filter((e) => matchesCategory(e, category)),\n\t\t[events, category],\n\t);\n\n\tconst statusCls = `sse-status sse-${status}`;\n\tconst statusLabel =\n\t\tstatus === \"connected\"\n\t\t\t? \"connected\"\n\t\t\t: status === \"connecting\"\n\t\t\t\t? \"connecting…\"\n\t\t\t\t: status === \"error\"\n\t\t\t\t\t? \"error\"\n\t\t\t\t\t: \"disconnected\";\n\n\treturn (\n\t\t<div className=\"events-tab\">\n\t\t\t<div className=\"events-header\">\n\t\t\t\t<span className={statusCls} role=\"status\">\n\t\t\t\t\t<span className=\"sse-dot\" aria-hidden=\"true\" />\n\t\t\t\t\t{statusLabel}\n\t\t\t\t</span>\n\t\t\t\t<span className=\"event-count\">{eventCount} events received</span>\n\t\t\t</div>\n\t\t\t<EventCategoryFilter active={category} onFilter={setCategory} />\n\t\t\t<EventStream events={filtered} />\n\t\t</div>\n\t);\n}\n"],"names":["TYPE_COLORS","FILTER_TYPES","summarize","ev","formatTs","ts","EventStream","events","filter","setFilter","useState","expanded","setExpanded","filtered","useMemo","e","jsxs","t","jsx","idx","cls","CATEGORIES","EventCategoryFilter","active","onFilter","cat","COMPACT_TYPES","CONFIG_TYPES","CREW_TYPES","GAME_TYPES","matchesCategory","EventsTab","status","eventCount","useSSE","category","setCategory","statusCls","statusLabel"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{r as n,m,n as I,j as e,d as $}from"./index-
|
|
2
|
-
//# sourceMappingURL=GameTab-
|
|
1
|
+
import{r as n,m,n as I,j as e,d as $}from"./index-PRkilPXw.js";import{A as k}from"./AchievementTiles-BESF1HT1.js";function g(s){return(Math.round(s*10)/10).toString()+"%"}function b(s){return s!=null?new Date(s).toLocaleString():"—"}function M(s){return s&&typeof s=="object"?s:{}}function F(s){return s.split("/").pop()||s}function p({title:s,rows:u,badge:i,renderValue:v}){return e.jsxs("div",{className:"lb-card",children:[e.jsxs("h3",{children:[s,i]}),u.length===0?e.jsx("div",{className:"lb-empty",children:"—"}):e.jsx("table",{children:e.jsx("tbody",{children:u.map((o,x)=>e.jsxs("tr",{children:[e.jsx("td",{title:o.repo_root,children:F(o.repo_root)}),e.jsx("td",{className:"num",children:v(o)})]},`${o.repo_root}-${x}`))})})]})}function B(){const[s,u]=n.useState({}),[i,v]=n.useState(0),[o,x]=n.useState([]),[T,R]=n.useState(!1),[_,G]=n.useState(!0),[w,N]=n.useState(null),j=n.useRef(0),y=n.useCallback(async()=>{try{const[t,a,c,l,D,H]=await Promise.all([m({metric:"cache",limit:25}),m({metric:"dedupe",limit:25}),m({metric:"turns",limit:25}),m({metric:"mega_cache",limit:25}),m({metric:"repos",limit:1}),I()]),E=l.find(f=>f.ts>j.current&&f.value>100);j.current>0&&E&&(N(`oopsie! cache went to ${Math.round(E.value)}% — MEGA CACHE 🥧`),setTimeout(()=>N(null),4e3)),l.length>0&&(j.current=Math.max(...l.map(f=>f.ts))),u({cache:t,dedupe:a,turns:c,mega_cache:l}),v(D[0]?.value??0),x(H)}catch{}finally{G(!1)}},[]);n.useEffect(()=>{let t=!0;const a=async()=>{t&&await y()};a();const c=setInterval(()=>void a(),15e3);return()=>{t=!1,clearInterval(c)}},[y]),n.useEffect(()=>{let t=!0;const a=async()=>{try{const l=await $();t&&(R(!!l.game_mode_on),l.theme&&(document.documentElement.dataset.theme=l.theme))}catch{}};a();const c=setInterval(()=>void a(),15e3);return()=>{t=!1,clearInterval(c)}},[]);const S=s.mega_cache??[];let r=null,d=null;for(const t of S){(r==null||t.value>r)&&(r=t.value);const a=M(t.meta),c=a.firstSeenTs??a.firstSeen??t.ts;(d==null||c<d)&&(d=c)}const A=r!=null&&r>100,h=s.turns??[],L=h.reduce((t,a)=>Math.max(t,a.value),0),V=Math.floor(Math.log2(L+1))+1,C=(s.cache?.length??0)>0||(s.dedupe?.length??0)>0||h.length>0||S.length>0||i>0;return e.jsxs("div",{className:`game-tab${T?" game-mode-on":""}`,children:[w!=null&&e.jsx("div",{className:"mega-cache-toast show",children:w}),A&&e.jsxs("div",{className:"mega-cache-banner",children:["🥧"," MEGA CACHE! peak ",g(r)," — first reached ",b(d)]}),A&&e.jsxs("div",{className:"achievement-tile unlocked",children:["🏆"," Opie's Wild Ride",e.jsxs("span",{className:"ach-detail",children:["best ",g(r)," · first ",b(d)]})]}),e.jsx("h2",{children:"High Scores"}),!C&&!_&&e.jsx("div",{className:"game-empty",children:"No scores yet — run a session with game mode on."}),C&&e.jsxs("div",{className:"game-leaderboards",children:[e.jsx(p,{title:"Cache %",rows:s.cache??[],renderValue:t=>g(t.value)}),e.jsx(p,{title:"Dedupe (collapsed)",rows:s.dedupe??[],renderValue:t=>t.value.toLocaleString()}),e.jsx(p,{title:"Turns",rows:h,badge:h.length>0?e.jsxs("span",{className:"lvl-badge",children:["LVL ",V]}):void 0,renderValue:t=>t.value.toLocaleString()}),e.jsx(p,{title:"MEGA CACHE trophies",rows:S,renderValue:t=>e.jsxs(e.Fragment,{children:[g(t.value),e.jsx("span",{className:"lb-meta",children:b(M(t.meta).firstSeenTs??M(t.meta).firstSeen??t.ts)})]})})]}),i>0&&e.jsx("div",{className:"repos-badge-line",children:e.jsxs("span",{className:"repos-badge",children:[i," repos"]})}),e.jsx("h3",{children:"Achievements"}),e.jsx(k,{achievements:o})]})}export{B as default};
|
|
2
|
+
//# sourceMappingURL=GameTab-BqneNRYH.js.map
|
package/extensions/dashboard-client/dist/assets/{GameTab-CJAX_th_.js.map → GameTab-BqneNRYH.js.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GameTab-CJAX_th_.js","sources":["../../src/tabs/GameTab.tsx"],"sourcesContent":["/**\n * dashboard-client/src/tabs/GameTab.tsx — Game tab (NEW).\n *\n * Renders:\n * (1) MEGA CACHE banner (animated gradient, shown when any mega_cache > 100)\n * (2) Opie unlock tile (shown when mega_cache > 100)\n * (3) High Scores header\n * (4) 4 leaderboards in 2×2 grid: Cache %, Dedupe, Turns (LVL badge),\n * MEGA CACHE trophies (firstSeen date meta)\n * (5) Repos count badge via fetchGameScores({metric:'repos',limit:1})\n * (6) Achievements sub-section (AchievementTiles shared component)\n * (7) Game empty state\n * Polls scores + achievements every 15s, game-state every 15s.\n * Applies data-theme accent vars when game_mode_on.\n */\n\nimport type React from \"react\";\nimport { useState, useEffect, useCallback, useRef } from \"react\";\nimport type { GameScoreRow, AchievementRow } from \"@contracts\";\nimport { fetchGameScores, fetchAchievements, fetchGameState } from \"../api/client\";\nimport { AchievementTiles } from \"../components/AchievementTiles\";\n\n/** Actual flat game state shape from the server (NOT the nested contract). */\ninterface FlatGameState {\n\tgame_mode_on: boolean;\n\ttheme: string;\n\ttui_display_mode: \"full\" | \"minimal\";\n}\n\n/** Metadata object inside a mega_cache GameScoreRow. */\ninterface TrophyMeta {\n\tfirstSeenTs?: number;\n\tfirstSeen?: number;\n}\n\n/* ── Format helpers (matching html.ts logic) ─────────────────────────── */\n\nfunction fmtPct(v: number): string {\n\treturn (Math.round(v * 10) / 10).toString() + \"%\";\n}\n\nfunction fmtDate(ts: number | null | undefined): string {\n\treturn ts != null ? new Date(ts).toLocaleString() : \"\\u2014\";\n}\n\nfunction trophyMeta(m: unknown): TrophyMeta {\n\treturn m && typeof m === \"object\" ? (m as TrophyMeta) : {};\n}\n\nfunction repoBasename(path: string): string {\n\treturn path.split(\"/\").pop() || path;\n}\n\n/* ── Leaderboard card ────────────────────────────────────────────────── */\n\ninterface LeaderboardCardProps {\n\ttitle: string;\n\trows: GameScoreRow[];\n\tbadge?: React.ReactNode;\n\trenderValue: (r: GameScoreRow) => React.ReactNode;\n}\n\nfunction LeaderboardCard({\n\ttitle,\n\trows,\n\tbadge,\n\trenderValue,\n}: LeaderboardCardProps): React.ReactElement {\n\treturn (\n\t\t<div className=\"lb-card\">\n\t\t\t<h3>\n\t\t\t\t{title}\n\t\t\t\t{badge}\n\t\t\t</h3>\n\t\t\t{rows.length === 0 ? (\n\t\t\t\t<div className=\"lb-empty\">—</div>\n\t\t\t) : (\n\t\t\t\t<table>\n\t\t\t\t\t<tbody>\n\t\t\t\t\t\t{rows.map((r, i) => (\n\t\t\t\t\t\t\t<tr key={`${r.repo_root}-${i}`}>\n\t\t\t\t\t\t\t\t<td title={r.repo_root}>{repoBasename(r.repo_root)}</td>\n\t\t\t\t\t\t\t\t<td className=\"num\">{renderValue(r)}</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)}\n\t\t</div>\n\t);\n}\n\n/* ── Main GameTab component ──────────────────────────────────────────── */\n\nexport default function GameTab(): React.ReactElement {\n\tconst [scores, setScores] = useState<\n\t\tRecord<string, GameScoreRow[]>\n\t>({});\n\tconst [reposCount, setReposCount] = useState(0);\n\tconst [achievements, setAchievements] = useState<AchievementRow[]>([]);\n\tconst [gameModeOn, setGameModeOn] = useState(false);\n\tconst [loading, setLoading] = useState(true);\n\tconst [megaToast, setMegaToast] = useState<string | null>(null);\n\tconst lastMegaTsRef = useRef(0);\n\n\tconst fetchAll = useCallback(async (): Promise<void> => {\n\t\ttry {\n\t\t\tconst [cache, dedupe, turns, megaCache, repos, ach] =\n\t\t\t\tawait Promise.all([\n\t\t\t\t\tfetchGameScores({ metric: \"cache\", limit: 25 }),\n\t\t\t\t\tfetchGameScores({ metric: \"dedupe\", limit: 25 }),\n\t\t\t\t\tfetchGameScores({ metric: \"turns\", limit: 25 }),\n\t\t\t\t\tfetchGameScores({ metric: \"mega_cache\", limit: 25 }),\n\t\t\t\t\tfetchGameScores({ metric: \"repos\", limit: 1 }),\n\t\t\t\t\tfetchAchievements(),\n\t\t\t\t]);\n\n\t\t\t/* Mega-cache toast: new row with value > 100 since last poll */\n\t\t\tconst newRow = megaCache.find(\n\t\t\t\t(r) => r.ts > lastMegaTsRef.current && r.value > 100,\n\t\t\t);\n\t\t\tif (lastMegaTsRef.current > 0 && newRow) {\n\t\t\t\tsetMegaToast(\n\t\t\t\t\t`oopsie! cache went to ${Math.round(newRow.value)}% — MEGA CACHE \\uD83E\\uDD67`,\n\t\t\t\t);\n\t\t\t\tsetTimeout(() => setMegaToast(null), 4000);\n\t\t\t}\n\t\t\tif (megaCache.length > 0) {\n\t\t\t\tlastMegaTsRef.current = Math.max(\n\t\t\t\t\t...megaCache.map((r) => r.ts),\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tsetScores({ cache, dedupe, turns, mega_cache: megaCache });\n\t\t\tsetReposCount(repos[0]?.value ?? 0);\n\t\t\tsetAchievements(ach);\n\t\t} catch {\n\t\t\t/* non-fatal */\n\t\t} finally {\n\t\t\tsetLoading(false);\n\t\t}\n\t}, []);\n\n\t/* Poll scores + achievements every 15s. */\n\tuseEffect(() => {\n\t\tlet active = true;\n\t\tconst doFetch = async (): Promise<void> => {\n\t\t\tif (active) await fetchAll();\n\t\t};\n\t\tvoid doFetch();\n\t\tconst timer = setInterval(() => void doFetch(), 15000);\n\t\treturn () => {\n\t\t\tactive = false;\n\t\t\tclearInterval(timer);\n\t\t};\n\t}, [fetchAll]);\n\n\t/* Poll game state every 15s for styling. */\n\tuseEffect(() => {\n\t\tlet active = true;\n\t\tconst doFetch = async (): Promise<void> => {\n\t\t\ttry {\n\t\t\t\tconst gs = (await fetchGameState()) as unknown as FlatGameState;\n\t\t\t\tif (active) {\n\t\t\t\t\tsetGameModeOn(!!gs.game_mode_on);\n\t\t\t\t\tif (gs.theme)\n\t\t\t\t\t\tdocument.documentElement.dataset.theme = gs.theme;\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t/* non-fatal */\n\t\t\t}\n\t\t};\n\t\tvoid doFetch();\n\t\tconst timer = setInterval(() => void doFetch(), 15000);\n\t\treturn () => {\n\t\t\tactive = false;\n\t\t\tclearInterval(timer);\n\t\t};\n\t}, []);\n\n\t/* Compute mega-cache banner data */\n\tconst megaRows = scores.mega_cache ?? [];\n\tlet bestMega: number | null = null;\n\tlet firstSeen: number | null = null;\n\tfor (const r of megaRows) {\n\t\tif (bestMega == null || r.value > bestMega) bestMega = r.value;\n\t\tconst m = trophyMeta(r.meta);\n\t\tconst fs = m.firstSeenTs ?? m.firstSeen ?? r.ts;\n\t\tif (firstSeen == null || fs < firstSeen) firstSeen = fs;\n\t}\n\tconst showMega = bestMega != null && bestMega > 100;\n\n\t/* Turns level badge */\n\tconst turnsRows = scores.turns ?? [];\n\tconst maxTurns = turnsRows.reduce((mx, r) => Math.max(mx, r.value), 0);\n\tconst lvl = Math.floor(Math.log2(maxTurns + 1)) + 1;\n\n\t/* Game empty state */\n\tconst hasData =\n\t\t(scores.cache?.length ?? 0) > 0 ||\n\t\t(scores.dedupe?.length ?? 0) > 0 ||\n\t\tturnsRows.length > 0 ||\n\t\tmegaRows.length > 0 ||\n\t\treposCount > 0;\n\n\treturn (\n\t\t<div className={`game-tab${gameModeOn ? \" game-mode-on\" : \"\"}`}>\n\t\t\t{megaToast != null && (\n\t\t\t\t<div className=\"mega-cache-toast show\">{megaToast}</div>\n\t\t\t)}\n\n\t\t\t{showMega && (\n\t\t\t\t<div className=\"mega-cache-banner\">\n\t\t\t\t\t{\"\\uD83E\\uDD67\"} MEGA CACHE! peak {fmtPct(bestMega!)} — first\n\t\t\t\t\treached {fmtDate(firstSeen)}\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{showMega && (\n\t\t\t\t<div className=\"achievement-tile unlocked\">\n\t\t\t\t\t{\"\\uD83C\\uDFC6\"} Opie's Wild Ride\n\t\t\t\t\t<span className=\"ach-detail\">\n\t\t\t\t\t\tbest {fmtPct(bestMega!)} · first {fmtDate(firstSeen)}\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t<h2>High Scores</h2>\n\n\t\t\t{!hasData && !loading && (\n\t\t\t\t<div className=\"game-empty\">\n\t\t\t\t\tNo scores yet — run a session with game mode on.\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{hasData && (\n\t\t\t\t<div className=\"game-leaderboards\">\n\t\t\t\t\t<LeaderboardCard\n\t\t\t\t\t\ttitle=\"Cache %\"\n\t\t\t\t\t\trows={scores.cache ?? []}\n\t\t\t\t\t\trenderValue={(r) => fmtPct(r.value)}\n\t\t\t\t\t/>\n\t\t\t\t\t<LeaderboardCard\n\t\t\t\t\t\ttitle=\"Dedupe (collapsed)\"\n\t\t\t\t\t\trows={scores.dedupe ?? []}\n\t\t\t\t\t\trenderValue={(r) => r.value.toLocaleString()}\n\t\t\t\t\t/>\n\t\t\t\t\t<LeaderboardCard\n\t\t\t\t\t\ttitle=\"Turns\"\n\t\t\t\t\t\trows={turnsRows}\n\t\t\t\t\t\tbadge={\n\t\t\t\t\t\t\tturnsRows.length > 0 ? (\n\t\t\t\t\t\t\t\t<span className=\"lvl-badge\">LVL {lvl}</span>\n\t\t\t\t\t\t\t) : undefined\n\t\t\t\t\t\t}\n\t\t\t\t\t\trenderValue={(r) => r.value.toLocaleString()}\n\t\t\t\t\t/>\n\t\t\t\t\t<LeaderboardCard\n\t\t\t\t\t\ttitle=\"MEGA CACHE trophies\"\n\t\t\t\t\t\trows={megaRows}\n\t\t\t\t\t\trenderValue={(r) => (\n\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t{fmtPct(r.value)}\n\t\t\t\t\t\t\t\t<span className=\"lb-meta\">\n\t\t\t\t\t\t\t\t\t{fmtDate(\n\t\t\t\t\t\t\t\t\t\ttrophyMeta(r.meta).firstSeenTs ??\n\t\t\t\t\t\t\t\t\t\t\ttrophyMeta(r.meta).firstSeen ??\n\t\t\t\t\t\t\t\t\t\t\tr.ts,\n\t\t\t\t\t\t\t\t\t)}\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)}\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{reposCount > 0 && (\n\t\t\t\t<div className=\"repos-badge-line\">\n\t\t\t\t\t<span className=\"repos-badge\">{reposCount} repos</span>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t<h3>Achievements</h3>\n\t\t\t<AchievementTiles achievements={achievements} />\n\t\t</div>\n\t);\n}\n"],"names":["fmtPct","v","fmtDate","ts","trophyMeta","m","repoBasename","path","LeaderboardCard","title","rows","badge","renderValue","jsxs","jsx","r","i","GameTab","scores","setScores","useState","reposCount","setReposCount","achievements","setAchievements","gameModeOn","setGameModeOn","loading","setLoading","megaToast","setMegaToast","lastMegaTsRef","useRef","fetchAll","useCallback","cache","dedupe","turns","megaCache","repos","ach","fetchGameScores","fetchAchievements","newRow","useEffect","active","doFetch","timer","gs","fetchGameState","megaRows","bestMega","firstSeen","fs","showMega","turnsRows","maxTurns","mx","lvl","hasData","Fragment","AchievementTiles"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"GameTab-BqneNRYH.js","sources":["../../src/tabs/GameTab.tsx"],"sourcesContent":["/**\n * dashboard-client/src/tabs/GameTab.tsx — Game tab (NEW).\n *\n * Renders:\n * (1) MEGA CACHE banner (animated gradient, shown when any mega_cache > 100)\n * (2) Opie unlock tile (shown when mega_cache > 100)\n * (3) High Scores header\n * (4) 4 leaderboards in 2×2 grid: Cache %, Dedupe, Turns (LVL badge),\n * MEGA CACHE trophies (firstSeen date meta)\n * (5) Repos count badge via fetchGameScores({metric:'repos',limit:1})\n * (6) Achievements sub-section (AchievementTiles shared component)\n * (7) Game empty state\n * Polls scores + achievements every 15s, game-state every 15s.\n * Applies data-theme accent vars when game_mode_on.\n */\n\nimport type React from \"react\";\nimport { useState, useEffect, useCallback, useRef } from \"react\";\nimport type { GameScoreRow, AchievementRow } from \"@contracts\";\nimport { fetchGameScores, fetchAchievements, fetchGameState } from \"../api/client\";\nimport { AchievementTiles } from \"../components/AchievementTiles\";\n\n/** Actual flat game state shape from the server (NOT the nested contract). */\ninterface FlatGameState {\n\tgame_mode_on: boolean;\n\ttheme: string;\n\ttui_display_mode: \"full\" | \"minimal\";\n}\n\n/** Metadata object inside a mega_cache GameScoreRow. */\ninterface TrophyMeta {\n\tfirstSeenTs?: number;\n\tfirstSeen?: number;\n}\n\n/* ── Format helpers (matching html.ts logic) ─────────────────────────── */\n\nfunction fmtPct(v: number): string {\n\treturn (Math.round(v * 10) / 10).toString() + \"%\";\n}\n\nfunction fmtDate(ts: number | null | undefined): string {\n\treturn ts != null ? new Date(ts).toLocaleString() : \"\\u2014\";\n}\n\nfunction trophyMeta(m: unknown): TrophyMeta {\n\treturn m && typeof m === \"object\" ? (m as TrophyMeta) : {};\n}\n\nfunction repoBasename(path: string): string {\n\treturn path.split(\"/\").pop() || path;\n}\n\n/* ── Leaderboard card ────────────────────────────────────────────────── */\n\ninterface LeaderboardCardProps {\n\ttitle: string;\n\trows: GameScoreRow[];\n\tbadge?: React.ReactNode;\n\trenderValue: (r: GameScoreRow) => React.ReactNode;\n}\n\nfunction LeaderboardCard({\n\ttitle,\n\trows,\n\tbadge,\n\trenderValue,\n}: LeaderboardCardProps): React.ReactElement {\n\treturn (\n\t\t<div className=\"lb-card\">\n\t\t\t<h3>\n\t\t\t\t{title}\n\t\t\t\t{badge}\n\t\t\t</h3>\n\t\t\t{rows.length === 0 ? (\n\t\t\t\t<div className=\"lb-empty\">—</div>\n\t\t\t) : (\n\t\t\t\t<table>\n\t\t\t\t\t<tbody>\n\t\t\t\t\t\t{rows.map((r, i) => (\n\t\t\t\t\t\t\t<tr key={`${r.repo_root}-${i}`}>\n\t\t\t\t\t\t\t\t<td title={r.repo_root}>{repoBasename(r.repo_root)}</td>\n\t\t\t\t\t\t\t\t<td className=\"num\">{renderValue(r)}</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)}\n\t\t</div>\n\t);\n}\n\n/* ── Main GameTab component ──────────────────────────────────────────── */\n\nexport default function GameTab(): React.ReactElement {\n\tconst [scores, setScores] = useState<\n\t\tRecord<string, GameScoreRow[]>\n\t>({});\n\tconst [reposCount, setReposCount] = useState(0);\n\tconst [achievements, setAchievements] = useState<AchievementRow[]>([]);\n\tconst [gameModeOn, setGameModeOn] = useState(false);\n\tconst [loading, setLoading] = useState(true);\n\tconst [megaToast, setMegaToast] = useState<string | null>(null);\n\tconst lastMegaTsRef = useRef(0);\n\n\tconst fetchAll = useCallback(async (): Promise<void> => {\n\t\ttry {\n\t\t\tconst [cache, dedupe, turns, megaCache, repos, ach] =\n\t\t\t\tawait Promise.all([\n\t\t\t\t\tfetchGameScores({ metric: \"cache\", limit: 25 }),\n\t\t\t\t\tfetchGameScores({ metric: \"dedupe\", limit: 25 }),\n\t\t\t\t\tfetchGameScores({ metric: \"turns\", limit: 25 }),\n\t\t\t\t\tfetchGameScores({ metric: \"mega_cache\", limit: 25 }),\n\t\t\t\t\tfetchGameScores({ metric: \"repos\", limit: 1 }),\n\t\t\t\t\tfetchAchievements(),\n\t\t\t\t]);\n\n\t\t\t/* Mega-cache toast: new row with value > 100 since last poll */\n\t\t\tconst newRow = megaCache.find(\n\t\t\t\t(r) => r.ts > lastMegaTsRef.current && r.value > 100,\n\t\t\t);\n\t\t\tif (lastMegaTsRef.current > 0 && newRow) {\n\t\t\t\tsetMegaToast(\n\t\t\t\t\t`oopsie! cache went to ${Math.round(newRow.value)}% — MEGA CACHE \\uD83E\\uDD67`,\n\t\t\t\t);\n\t\t\t\tsetTimeout(() => setMegaToast(null), 4000);\n\t\t\t}\n\t\t\tif (megaCache.length > 0) {\n\t\t\t\tlastMegaTsRef.current = Math.max(\n\t\t\t\t\t...megaCache.map((r) => r.ts),\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tsetScores({ cache, dedupe, turns, mega_cache: megaCache });\n\t\t\tsetReposCount(repos[0]?.value ?? 0);\n\t\t\tsetAchievements(ach);\n\t\t} catch {\n\t\t\t/* non-fatal */\n\t\t} finally {\n\t\t\tsetLoading(false);\n\t\t}\n\t}, []);\n\n\t/* Poll scores + achievements every 15s. */\n\tuseEffect(() => {\n\t\tlet active = true;\n\t\tconst doFetch = async (): Promise<void> => {\n\t\t\tif (active) await fetchAll();\n\t\t};\n\t\tvoid doFetch();\n\t\tconst timer = setInterval(() => void doFetch(), 15000);\n\t\treturn () => {\n\t\t\tactive = false;\n\t\t\tclearInterval(timer);\n\t\t};\n\t}, [fetchAll]);\n\n\t/* Poll game state every 15s for styling. */\n\tuseEffect(() => {\n\t\tlet active = true;\n\t\tconst doFetch = async (): Promise<void> => {\n\t\t\ttry {\n\t\t\t\tconst gs = (await fetchGameState()) as unknown as FlatGameState;\n\t\t\t\tif (active) {\n\t\t\t\t\tsetGameModeOn(!!gs.game_mode_on);\n\t\t\t\t\tif (gs.theme)\n\t\t\t\t\t\tdocument.documentElement.dataset.theme = gs.theme;\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t/* non-fatal */\n\t\t\t}\n\t\t};\n\t\tvoid doFetch();\n\t\tconst timer = setInterval(() => void doFetch(), 15000);\n\t\treturn () => {\n\t\t\tactive = false;\n\t\t\tclearInterval(timer);\n\t\t};\n\t}, []);\n\n\t/* Compute mega-cache banner data */\n\tconst megaRows = scores.mega_cache ?? [];\n\tlet bestMega: number | null = null;\n\tlet firstSeen: number | null = null;\n\tfor (const r of megaRows) {\n\t\tif (bestMega == null || r.value > bestMega) bestMega = r.value;\n\t\tconst m = trophyMeta(r.meta);\n\t\tconst fs = m.firstSeenTs ?? m.firstSeen ?? r.ts;\n\t\tif (firstSeen == null || fs < firstSeen) firstSeen = fs;\n\t}\n\tconst showMega = bestMega != null && bestMega > 100;\n\n\t/* Turns level badge */\n\tconst turnsRows = scores.turns ?? [];\n\tconst maxTurns = turnsRows.reduce((mx, r) => Math.max(mx, r.value), 0);\n\tconst lvl = Math.floor(Math.log2(maxTurns + 1)) + 1;\n\n\t/* Game empty state */\n\tconst hasData =\n\t\t(scores.cache?.length ?? 0) > 0 ||\n\t\t(scores.dedupe?.length ?? 0) > 0 ||\n\t\tturnsRows.length > 0 ||\n\t\tmegaRows.length > 0 ||\n\t\treposCount > 0;\n\n\treturn (\n\t\t<div className={`game-tab${gameModeOn ? \" game-mode-on\" : \"\"}`}>\n\t\t\t{megaToast != null && (\n\t\t\t\t<div className=\"mega-cache-toast show\">{megaToast}</div>\n\t\t\t)}\n\n\t\t\t{showMega && (\n\t\t\t\t<div className=\"mega-cache-banner\">\n\t\t\t\t\t{\"\\uD83E\\uDD67\"} MEGA CACHE! peak {fmtPct(bestMega!)} — first\n\t\t\t\t\treached {fmtDate(firstSeen)}\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{showMega && (\n\t\t\t\t<div className=\"achievement-tile unlocked\">\n\t\t\t\t\t{\"\\uD83C\\uDFC6\"} Opie's Wild Ride\n\t\t\t\t\t<span className=\"ach-detail\">\n\t\t\t\t\t\tbest {fmtPct(bestMega!)} · first {fmtDate(firstSeen)}\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t<h2>High Scores</h2>\n\n\t\t\t{!hasData && !loading && (\n\t\t\t\t<div className=\"game-empty\">\n\t\t\t\t\tNo scores yet — run a session with game mode on.\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{hasData && (\n\t\t\t\t<div className=\"game-leaderboards\">\n\t\t\t\t\t<LeaderboardCard\n\t\t\t\t\t\ttitle=\"Cache %\"\n\t\t\t\t\t\trows={scores.cache ?? []}\n\t\t\t\t\t\trenderValue={(r) => fmtPct(r.value)}\n\t\t\t\t\t/>\n\t\t\t\t\t<LeaderboardCard\n\t\t\t\t\t\ttitle=\"Dedupe (collapsed)\"\n\t\t\t\t\t\trows={scores.dedupe ?? []}\n\t\t\t\t\t\trenderValue={(r) => r.value.toLocaleString()}\n\t\t\t\t\t/>\n\t\t\t\t\t<LeaderboardCard\n\t\t\t\t\t\ttitle=\"Turns\"\n\t\t\t\t\t\trows={turnsRows}\n\t\t\t\t\t\tbadge={\n\t\t\t\t\t\t\tturnsRows.length > 0 ? (\n\t\t\t\t\t\t\t\t<span className=\"lvl-badge\">LVL {lvl}</span>\n\t\t\t\t\t\t\t) : undefined\n\t\t\t\t\t\t}\n\t\t\t\t\t\trenderValue={(r) => r.value.toLocaleString()}\n\t\t\t\t\t/>\n\t\t\t\t\t<LeaderboardCard\n\t\t\t\t\t\ttitle=\"MEGA CACHE trophies\"\n\t\t\t\t\t\trows={megaRows}\n\t\t\t\t\t\trenderValue={(r) => (\n\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t{fmtPct(r.value)}\n\t\t\t\t\t\t\t\t<span className=\"lb-meta\">\n\t\t\t\t\t\t\t\t\t{fmtDate(\n\t\t\t\t\t\t\t\t\t\ttrophyMeta(r.meta).firstSeenTs ??\n\t\t\t\t\t\t\t\t\t\t\ttrophyMeta(r.meta).firstSeen ??\n\t\t\t\t\t\t\t\t\t\t\tr.ts,\n\t\t\t\t\t\t\t\t\t)}\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)}\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{reposCount > 0 && (\n\t\t\t\t<div className=\"repos-badge-line\">\n\t\t\t\t\t<span className=\"repos-badge\">{reposCount} repos</span>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t<h3>Achievements</h3>\n\t\t\t<AchievementTiles achievements={achievements} />\n\t\t</div>\n\t);\n}\n"],"names":["fmtPct","v","fmtDate","ts","trophyMeta","m","repoBasename","path","LeaderboardCard","title","rows","badge","renderValue","jsxs","jsx","r","i","GameTab","scores","setScores","useState","reposCount","setReposCount","achievements","setAchievements","gameModeOn","setGameModeOn","loading","setLoading","megaToast","setMegaToast","lastMegaTsRef","useRef","fetchAll","useCallback","cache","dedupe","turns","megaCache","repos","ach","fetchGameScores","fetchAchievements","newRow","useEffect","active","doFetch","timer","gs","fetchGameState","megaRows","bestMega","firstSeen","fs","showMega","turnsRows","maxTurns","mx","lvl","hasData","Fragment","AchievementTiles"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{u as x,r as d,B as j,C as g,j as e,D as b}from"./index-
|
|
2
|
-
//# sourceMappingURL=MaintenanceTab-
|
|
1
|
+
import{u as x,r as d,B as j,C as g,j as e,D as b}from"./index-PRkilPXw.js";const N=[{key:"integrity-check",label:"Integrity Check",desc:"Run PRAGMA integrity_check",dangerous:!1},{key:"reconcile-dedup",label:"Reconcile Dedup Mirror",desc:"Fix orphan dedup rows + backfill refs",dangerous:!1,confirm:"Reconcile dedup_mirror (safe read/write)?"},{key:"checkpoint",label:"WAL Checkpoint",desc:"Merge WAL into main DB (read-only)",dangerous:!1},{key:"vacuum",label:"VACUUM",desc:"Rebuild DB reclaiming free pages",dangerous:!0,confirm:"VACUUM rewrites the entire DB. Continue?"},{key:"reindex",label:"REINDEX",desc:"Rebuild all indexes",dangerous:!0,confirm:"REINDEX rebuilds indexes. Continue?"},{key:"fts5-rebuild",label:"FTS5 Rebuild",desc:"Rebuild full-text search index",dangerous:!0,confirm:"Rebuild FTS5 trigram index. Continue?"},{key:"prune",label:"Prune Old Rows",desc:"Delete transcripts + epochs older than N days",dangerous:!0,daysOld:!0,confirm:"Delete old raw_transcript rows. Continue?"}];function o(s){return s<1024?`${s} B`:s<1024*1024?`${(s/1024).toFixed(1)} KB`:`${(s/(1024*1024)).toFixed(1)} MB`}function p({files:s,pageSize:t,pageCount:a,freelistPages:r}){const l=s.dbBytes+s.walBytes+s.shmBytes;return e.jsxs("div",{className:"stat-section",children:[e.jsx("div",{className:"stat-label",children:"File Sizes"}),e.jsx("table",{className:"compact-table",children:e.jsxs("tbody",{children:[e.jsxs("tr",{children:[e.jsx("td",{children:"sqlite.db"}),e.jsx("td",{className:"num",children:o(s.dbBytes)})]}),s.walBytes>0&&e.jsxs("tr",{children:[e.jsx("td",{children:".wal"}),e.jsx("td",{className:"num",children:o(s.walBytes)})]}),s.shmBytes>0&&e.jsxs("tr",{children:[e.jsx("td",{children:".shm"}),e.jsx("td",{className:"num",children:o(s.shmBytes)})]}),e.jsxs("tr",{children:[e.jsx("td",{children:"Total"}),e.jsx("td",{className:"num",children:o(l)})]}),e.jsxs("tr",{children:[e.jsx("td",{children:"Page size"}),e.jsx("td",{className:"num",children:o(t)})]}),e.jsxs("tr",{children:[e.jsx("td",{children:"Pages"}),e.jsxs("td",{className:"num",children:[a.toLocaleString()," (",r.toLocaleString()," ","free)"]})]})]})})]})}function y({tables:s}){const t=s.reduce((a,r)=>a+Math.max(0,r.rowCount),0);return e.jsxs("div",{className:"stat-section",children:[e.jsxs("div",{className:"stat-label",children:["Table Row Counts (",t.toLocaleString()," total)"]}),e.jsxs("table",{className:"compact-table",children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{children:"Table"}),e.jsx("th",{className:"num",children:"Rows"})]})}),e.jsx("tbody",{children:s.map(a=>e.jsxs("tr",{children:[e.jsx("td",{children:a.table}),e.jsx("td",{className:"num",children:a.rowCount>=0?a.rowCount.toLocaleString():"—"})]},a.table))})]})]})}function C({data:s,loading:t,error:a}){return e.jsxs("div",{className:"card",children:[e.jsx("div",{className:"card-header",children:"DB Stats"}),e.jsxs("div",{className:"card-body",children:[t&&!s&&e.jsx("span",{className:"text-muted",children:"Loading…"}),a&&!s&&e.jsx("span",{className:"text-error",children:a.message}),s&&e.jsxs(e.Fragment,{children:[e.jsx(p,{files:s.storage.files,pageSize:s.storage.pageSize,pageCount:s.storage.pageCount,freelistPages:s.storage.freelistPages}),e.jsx(y,{tables:s.tables})]})]})]})}function k({columns:s}){const t=s.filter(a=>!a.present);return e.jsxs("div",{className:"stat-section",children:[e.jsxs("div",{className:"stat-label",children:["Column Audit (",s.length," columns, ",t.length," missing)"]}),e.jsxs("table",{className:"compact-table",children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{children:"Table"}),e.jsx("th",{children:"Column"}),e.jsx("th",{children:"Decl"}),e.jsx("th",{children:"Status"})]})}),e.jsx("tbody",{children:s.map(a=>e.jsxs("tr",{children:[e.jsx("td",{children:a.table}),e.jsx("td",{children:a.column}),e.jsx("td",{className:"text-mono",children:a.expectedDecl}),e.jsx("td",{className:a.present?"text-ok":"text-error",children:a.present?"✓":"MISSING"})]},`${a.table}.${a.column}`))})]})]})}function v({data:s,loading:t,error:a}){return e.jsxs("div",{className:"card",children:[e.jsxs("div",{className:"card-header",children:["Schema Health",s&&e.jsx("span",{className:`badge ${s.healthy?"badge-ok":"badge-bad"}`,children:s.healthy?"HEALTHY":"DEGRADED"})]}),e.jsxs("div",{className:"card-body",children:[t&&!s&&e.jsx("span",{className:"text-muted",children:"Loading…"}),a&&!s&&e.jsx("span",{className:"text-error",children:a.message}),s&&e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"stat-row",children:[e.jsx("span",{className:"stat-label",children:"Schema version"}),e.jsx("span",{className:"num",children:s.schemaVersion})]}),e.jsxs("div",{className:"stat-row",children:[e.jsx("span",{className:"stat-label",children:"Integrity"}),e.jsx("span",{className:s.integrity[0]==="ok"?"text-ok":"text-error",children:s.integrity.join("; ")})]}),e.jsx(k,{columns:s.columns}),s.fkCheck.length>0&&e.jsxs("div",{className:"stat-section",children:[e.jsxs("div",{className:"stat-label text-warn",children:["FK Check (",s.fkCheck.length,")"]}),s.fkCheck.map((r,l)=>e.jsx("div",{className:"text-warn text-mono",children:r},l))]})]})]})]})}function f(){const[s,t]=d.useState([]),[a,r]=d.useState(null),[l,m]=d.useState("30"),u=d.useCallback(async n=>{const h=n.daysOld?{action:"prune",daysOld:Number(l)}:{action:n.key};r(n.key);try{const c=await b(h);t(i=>[{...c,timestamp:Date.now()},...i].slice(0,20))}catch(c){t(i=>[{operation:n.key,success:!1,affected:0,reclaimedBytes:0,summary:c instanceof Error?c.message:String(c),timestamp:Date.now()},...i].slice(0,20))}finally{r(null)}},[l]);return e.jsxs("div",{className:"card",children:[e.jsx("div",{className:"card-header",children:"Actions"}),e.jsx("div",{className:"card-body",children:e.jsx("div",{className:"action-grid",children:N.map(n=>{const h=a===n.key,c=s.find(i=>i.operation===n.key);return e.jsxs("div",{className:"action-item",children:[e.jsxs("div",{className:"action-info",children:[e.jsxs("div",{className:"action-label",children:[n.label,n.dangerous&&e.jsx("span",{className:"badge badge-warn",children:"DESTRUCTIVE"})]}),e.jsx("div",{className:"action-desc",children:n.desc}),c&&e.jsx("div",{className:`action-result ${c.success?"text-ok":"text-error"}`,children:c.summary})]}),e.jsxs("div",{className:"action-controls",children:[n.daysOld&&e.jsx("input",{type:"number",min:1,max:365,value:l,onChange:i=>m(i.target.value),className:"days-input",disabled:h}),e.jsx("button",{className:`btn ${n.dangerous?"btn-danger":"btn-primary"}`,disabled:a!==null,onClick:()=>{n.confirm&&!window.confirm(n.confirm)||u(n)},children:h?"Running…":"Run"})]})]},n.key)})})})]})}function S(){const{data:s,loading:t,error:a}=x(d.useCallback(()=>j(),[]),{pollInterval:15e3}),{data:r,loading:l,error:m}=x(d.useCallback(()=>g(),[]),{pollInterval:1e4});return e.jsxs("div",{className:"maintenance-tab",children:[e.jsxs("div",{className:"card-grid",children:[e.jsx(v,{data:r,loading:l,error:m}),e.jsx(C,{data:s,loading:t,error:a})]}),e.jsx(f,{})]})}export{S as default};
|
|
2
|
+
//# sourceMappingURL=MaintenanceTab-LblMg3NB.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MaintenanceTab-P4SUsvMf.js","sources":["../../src/tabs/MaintenanceTab.tsx"],"sourcesContent":["/**\n * dashboard-client/src/tabs/MaintenanceTab.tsx — Maintenance tab (v0.11.2 S49B).\n *\n * Three cards:\n * 1. Schema Health (schema version + integrity + column audit)\n * 2. DB Stats (file sizes + table row counts)\n * 3. Actions (one-time maintenance operations with results history)\n *\n * All three fetch independently; failures in one do not block the others.\n */\n\nimport type React from \"react\";\nimport { useState, useCallback } from \"react\";\nimport type {\n\tDbStatsResponse,\n\tSchemaHealthResponse,\n\tMaintenanceAction,\n\tMaintenanceActionResult,\n} from \"@contracts\";\nimport { useApi } from \"../hooks/useApi\";\nimport {\n\tfetchDbStats,\n\tfetchSchemaHealth,\n\tpostMaintenanceAction,\n} from \"../api/client\";\n\n// ---------------------------------------------------------------------------\n// Action config\n// ---------------------------------------------------------------------------\n\ninterface ActionDef {\n\tkey: string;\n\tlabel: string;\n\tdesc: string;\n\tdangerous: boolean;\n\tconfirm?: string;\n\tdaysOld?: boolean;\n}\n\nconst ACTIONS: ActionDef[] = [\n\t{\n\t\tkey: \"integrity-check\",\n\t\tlabel: \"Integrity Check\",\n\t\tdesc: \"Run PRAGMA integrity_check\",\n\t\tdangerous: false,\n\t},\n\t{\n\t\tkey: \"reconcile-dedup\",\n\t\tlabel: \"Reconcile Dedup Mirror\",\n\t\tdesc: \"Fix orphan dedup rows + backfill refs\",\n\t\tdangerous: false,\n\t\tconfirm: \"Reconcile dedup_mirror (safe read/write)?\",\n\t},\n\t{\n\t\tkey: \"checkpoint\",\n\t\tlabel: \"WAL Checkpoint\",\n\t\tdesc: \"Merge WAL into main DB (read-only)\",\n\t\tdangerous: false,\n\t},\n\t{\n\t\tkey: \"vacuum\",\n\t\tlabel: \"VACUUM\",\n\t\tdesc: \"Rebuild DB reclaiming free pages\",\n\t\tdangerous: true,\n\t\tconfirm: \"VACUUM rewrites the entire DB. Continue?\",\n\t},\n\t{\n\t\tkey: \"reindex\",\n\t\tlabel: \"REINDEX\",\n\t\tdesc: \"Rebuild all indexes\",\n\t\tdangerous: true,\n\t\tconfirm: \"REINDEX rebuilds indexes. Continue?\",\n\t},\n\t{\n\t\tkey: \"fts5-rebuild\",\n\t\tlabel: \"FTS5 Rebuild\",\n\t\tdesc: \"Rebuild full-text search index\",\n\t\tdangerous: true,\n\t\tconfirm: \"Rebuild FTS5 trigram index. Continue?\",\n\t},\n\t{\n\t\tkey: \"prune\",\n\t\tlabel: \"Prune Old Rows\",\n\t\tdesc: \"Delete transcripts + epochs older than N days\",\n\t\tdangerous: true,\n\t\tdaysOld: true,\n\t\tconfirm: \"Delete old raw_transcript rows. Continue?\",\n\t},\n];\n\n// ---------------------------------------------------------------------------\n// Format helpers (local — matches html.ts fmtBytes)\n// ---------------------------------------------------------------------------\n\nfunction fmtBytes(b: number): string {\n\tif (b < 1024) return `${b} B`;\n\tif (b < 1024 * 1024) return `${(b / 1024).toFixed(1)} KB`;\n\treturn `${(b / (1024 * 1024)).toFixed(1)} MB`;\n}\n\n// ---------------------------------------------------------------------------\n// DB Stats card\n// ---------------------------------------------------------------------------\n\nfunction FileSizesCard({\n\tfiles,\n\tpageSize,\n\tpageCount,\n\tfreelistPages,\n}: {\n\tfiles: DbStatsResponse[\"storage\"][\"files\"];\n\tpageSize: number;\n\tpageCount: number;\n\tfreelistPages: number;\n}): React.ReactElement {\n\tconst total = files.dbBytes + files.walBytes + files.shmBytes;\n\treturn (\n\t\t<div className=\"stat-section\">\n\t\t\t<div className=\"stat-label\">File Sizes</div>\n\t\t\t<table className=\"compact-table\">\n\t\t\t\t<tbody>\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<td>sqlite.db</td>\n\t\t\t\t\t\t<td className=\"num\">{fmtBytes(files.dbBytes)}</td>\n\t\t\t\t\t</tr>\n\t\t\t\t\t{files.walBytes > 0 && (\n\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t<td>.wal</td>\n\t\t\t\t\t\t\t<td className=\"num\">{fmtBytes(files.walBytes)}</td>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t)}\n\t\t\t\t\t{files.shmBytes > 0 && (\n\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t<td>.shm</td>\n\t\t\t\t\t\t\t<td className=\"num\">{fmtBytes(files.shmBytes)}</td>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t)}\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<td>Total</td>\n\t\t\t\t\t\t<td className=\"num\">{fmtBytes(total)}</td>\n\t\t\t\t\t</tr>\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<td>Page size</td>\n\t\t\t\t\t\t<td className=\"num\">{fmtBytes(pageSize)}</td>\n\t\t\t\t\t</tr>\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<td>Pages</td>\n\t\t\t\t\t\t<td className=\"num\">\n\t\t\t\t\t\t\t{pageCount.toLocaleString()} ({freelistPages.toLocaleString()}{\" \"}\n\t\t\t\t\t\t\tfree)\n\t\t\t\t\t\t</td>\n\t\t\t\t\t</tr>\n\t\t\t\t</tbody>\n\t\t\t</table>\n\t\t</div>\n\t);\n}\n\nfunction TableRowCountsCard({\n\ttables,\n}: {\n\ttables: DbStatsResponse[\"tables\"];\n}): React.ReactElement {\n\tconst totalRows = tables.reduce((s, t) => s + Math.max(0, t.rowCount), 0);\n\treturn (\n\t\t<div className=\"stat-section\">\n\t\t\t<div className=\"stat-label\">\n\t\t\t\tTable Row Counts ({totalRows.toLocaleString()} total)\n\t\t\t</div>\n\t\t\t<table className=\"compact-table\">\n\t\t\t\t<thead>\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<th>Table</th>\n\t\t\t\t\t\t<th className=\"num\">Rows</th>\n\t\t\t\t\t</tr>\n\t\t\t\t</thead>\n\t\t\t\t<tbody>\n\t\t\t\t\t{tables.map((t) => (\n\t\t\t\t\t\t<tr key={t.table}>\n\t\t\t\t\t\t\t<td>{t.table}</td>\n\t\t\t\t\t\t\t<td className=\"num\">\n\t\t\t\t\t\t\t\t{t.rowCount >= 0 ? t.rowCount.toLocaleString() : \"—\"}\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t))}\n\t\t\t\t</tbody>\n\t\t\t</table>\n\t\t</div>\n\t);\n}\n\nfunction DbStatsCard({\n\tdata,\n\tloading,\n\terror,\n}: {\n\tdata: DbStatsResponse | null;\n\tloading: boolean;\n\terror: Error | null;\n}): React.ReactElement {\n\treturn (\n\t\t<div className=\"card\">\n\t\t\t<div className=\"card-header\">DB Stats</div>\n\t\t\t<div className=\"card-body\">\n\t\t\t\t{loading && !data && <span className=\"text-muted\">Loading…</span>}\n\t\t\t\t{error && !data && <span className=\"text-error\">{error.message}</span>}\n\t\t\t\t{data && (\n\t\t\t\t\t<>\n\t\t\t\t\t\t<FileSizesCard\n\t\t\t\t\t\t\tfiles={data.storage.files}\n\t\t\t\t\t\t\tpageSize={data.storage.pageSize}\n\t\t\t\t\t\t\tpageCount={data.storage.pageCount}\n\t\t\t\t\t\t\tfreelistPages={data.storage.freelistPages}\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t<TableRowCountsCard tables={data.tables} />\n\t\t\t\t\t</>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\n// ---------------------------------------------------------------------------\n// Schema Health card\n// ---------------------------------------------------------------------------\n\nfunction ColumnAuditCard({\n\tcolumns,\n}: {\n\tcolumns: SchemaHealthResponse[\"columns\"];\n}): React.ReactElement {\n\tconst missing = columns.filter((c) => !c.present);\n\treturn (\n\t\t<div className=\"stat-section\">\n\t\t\t<div className=\"stat-label\">\n\t\t\t\tColumn Audit ({columns.length} columns, {missing.length} missing)\n\t\t\t</div>\n\t\t\t<table className=\"compact-table\">\n\t\t\t\t<thead>\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<th>Table</th>\n\t\t\t\t\t\t<th>Column</th>\n\t\t\t\t\t\t<th>Decl</th>\n\t\t\t\t\t\t<th>Status</th>\n\t\t\t\t\t</tr>\n\t\t\t\t</thead>\n\t\t\t\t<tbody>\n\t\t\t\t\t{columns.map((c) => (\n\t\t\t\t\t\t<tr key={`${c.table}.${c.column}`}>\n\t\t\t\t\t\t\t<td>{c.table}</td>\n\t\t\t\t\t\t\t<td>{c.column}</td>\n\t\t\t\t\t\t\t<td className=\"text-mono\">{c.expectedDecl}</td>\n\t\t\t\t\t\t\t<td className={c.present ? \"text-ok\" : \"text-error\"}>\n\t\t\t\t\t\t\t\t{c.present ? \"✓\" : \"MISSING\"}\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t))}\n\t\t\t\t</tbody>\n\t\t\t</table>\n\t\t</div>\n\t);\n}\n\nfunction SchemaHealthCard({\n\tdata,\n\tloading,\n\terror,\n}: {\n\tdata: SchemaHealthResponse | null;\n\tloading: boolean;\n\terror: Error | null;\n}): React.ReactElement {\n\treturn (\n\t\t<div className=\"card\">\n\t\t\t<div className=\"card-header\">\n\t\t\t\tSchema Health\n\t\t\t\t{data && (\n\t\t\t\t\t<span className={`badge ${data.healthy ? \"badge-ok\" : \"badge-bad\"}`}>\n\t\t\t\t\t\t{data.healthy ? \"HEALTHY\" : \"DEGRADED\"}\n\t\t\t\t\t</span>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t\t<div className=\"card-body\">\n\t\t\t\t{loading && !data && <span className=\"text-muted\">Loading…</span>}\n\t\t\t\t{error && !data && <span className=\"text-error\">{error.message}</span>}\n\t\t\t\t{data && (\n\t\t\t\t\t<>\n\t\t\t\t\t\t<div className=\"stat-row\">\n\t\t\t\t\t\t\t<span className=\"stat-label\">Schema version</span>\n\t\t\t\t\t\t\t<span className=\"num\">{data.schemaVersion}</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div className=\"stat-row\">\n\t\t\t\t\t\t\t<span className=\"stat-label\">Integrity</span>\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tclassName={\n\t\t\t\t\t\t\t\t\tdata.integrity[0] === \"ok\" ? \"text-ok\" : \"text-error\"\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{data.integrity.join(\"; \")}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<ColumnAuditCard columns={data.columns} />\n\t\t\t\t\t\t{data.fkCheck.length > 0 && (\n\t\t\t\t\t\t\t<div className=\"stat-section\">\n\t\t\t\t\t\t\t\t<div className=\"stat-label text-warn\">\n\t\t\t\t\t\t\t\t\tFK Check ({data.fkCheck.length})\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t{data.fkCheck.map((line, i) => (\n\t\t\t\t\t\t\t\t\t<div key={i} className=\"text-warn text-mono\">\n\t\t\t\t\t\t\t\t\t\t{line}\n\t\t\t\t\t\t\t\t\t</div>\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\t\t\t\t\t</>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\n// ---------------------------------------------------------------------------\n// Actions card\n// ---------------------------------------------------------------------------\n\nfunction ActionsCard(): React.ReactElement {\n\tconst [results, setResults] = useState<MaintenanceActionResult[]>([]);\n\tconst [running, setRunning] = useState<string | null>(null);\n\tconst [days, setDays] = useState(\"30\");\n\n\tconst handleAction = useCallback(\n\t\tasync (def: ActionDef) => {\n\t\t\tconst action: MaintenanceAction = def.daysOld\n\t\t\t\t? { action: \"prune\", daysOld: Number(days) }\n\t\t\t\t: ({ action: def.key } as MaintenanceAction);\n\n\t\t\tsetRunning(def.key);\n\t\t\ttry {\n\t\t\t\tconst res = await postMaintenanceAction(action);\n\t\t\t\tsetResults((prev) =>\n\t\t\t\t\t[{ ...res, timestamp: Date.now() }, ...prev].slice(0, 20),\n\t\t\t\t);\n\t\t\t} catch (err) {\n\t\t\t\tsetResults((prev) =>\n\t\t\t\t\t[\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\toperation: def.key,\n\t\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\t\taffected: 0,\n\t\t\t\t\t\t\treclaimedBytes: 0,\n\t\t\t\t\t\t\tsummary: err instanceof Error ? err.message : String(err),\n\t\t\t\t\t\t\ttimestamp: Date.now(),\n\t\t\t\t\t\t},\n\t\t\t\t\t\t...prev,\n\t\t\t\t\t].slice(0, 20),\n\t\t\t\t);\n\t\t\t} finally {\n\t\t\t\tsetRunning(null);\n\t\t\t}\n\t\t},\n\t\t[days],\n\t);\n\n\treturn (\n\t\t<div className=\"card\">\n\t\t\t<div className=\"card-header\">Actions</div>\n\t\t\t<div className=\"card-body\">\n\t\t\t\t<div className=\"action-grid\">\n\t\t\t\t\t{ACTIONS.map((def) => {\n\t\t\t\t\t\tconst isRunning = running === def.key;\n\t\t\t\t\t\tconst lastResult = results.find((r) => r.operation === def.key);\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t<div key={def.key} className=\"action-item\">\n\t\t\t\t\t\t\t\t<div className=\"action-info\">\n\t\t\t\t\t\t\t\t\t<div className=\"action-label\">\n\t\t\t\t\t\t\t\t\t\t{def.label}\n\t\t\t\t\t\t\t\t\t\t{def.dangerous && (\n\t\t\t\t\t\t\t\t\t\t\t<span className=\"badge badge-warn\">DESTRUCTIVE</span>\n\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t<div className=\"action-desc\">{def.desc}</div>\n\t\t\t\t\t\t\t\t\t{lastResult && (\n\t\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\t\tclassName={`action-result ${lastResult.success ? \"text-ok\" : \"text-error\"}`}\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t{lastResult.summary}\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t<div className=\"action-controls\">\n\t\t\t\t\t\t\t\t\t{def.daysOld && (\n\t\t\t\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\t\t\t\ttype=\"number\"\n\t\t\t\t\t\t\t\t\t\t\tmin={1}\n\t\t\t\t\t\t\t\t\t\t\tmax={365}\n\t\t\t\t\t\t\t\t\t\t\tvalue={days}\n\t\t\t\t\t\t\t\t\t\t\tonChange={(e) => setDays(e.target.value)}\n\t\t\t\t\t\t\t\t\t\t\tclassName=\"days-input\"\n\t\t\t\t\t\t\t\t\t\t\tdisabled={isRunning}\n\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\tclassName={`btn ${def.dangerous ? \"btn-danger\" : \"btn-primary\"}`}\n\t\t\t\t\t\t\t\t\t\tdisabled={running !== null}\n\t\t\t\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\t\t\t\tif (def.confirm && !window.confirm(def.confirm)) return;\n\t\t\t\t\t\t\t\t\t\t\thandleAction(def);\n\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{isRunning ? \"Running…\" : \"Run\"}\n\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t);\n\t\t\t\t\t})}\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\n// ---------------------------------------------------------------------------\n// Main tab component\n// ---------------------------------------------------------------------------\n\nexport default function MaintenanceTab(): React.ReactElement {\n\tconst {\n\t\tdata: dbStats,\n\t\tloading: statsLoading,\n\t\terror: statsError,\n\t} = useApi<DbStatsResponse>(\n\t\tuseCallback(() => fetchDbStats(), []),\n\t\t{ pollInterval: 15000 },\n\t);\n\n\tconst {\n\t\tdata: schemaHealth,\n\t\tloading: healthLoading,\n\t\terror: healthError,\n\t} = useApi<SchemaHealthResponse>(\n\t\tuseCallback(() => fetchSchemaHealth(), []),\n\t\t{ pollInterval: 10000 },\n\t);\n\n\treturn (\n\t\t<div className=\"maintenance-tab\">\n\t\t\t<div className=\"card-grid\">\n\t\t\t\t<SchemaHealthCard\n\t\t\t\t\tdata={schemaHealth}\n\t\t\t\t\tloading={healthLoading}\n\t\t\t\t\terror={healthError}\n\t\t\t\t/>\n\t\t\t\t<DbStatsCard data={dbStats} loading={statsLoading} error={statsError} />\n\t\t\t</div>\n\t\t\t<ActionsCard />\n\t\t</div>\n\t);\n}\n"],"names":["ACTIONS","fmtBytes","b","FileSizesCard","files","pageSize","pageCount","freelistPages","total","jsxs","jsx","TableRowCountsCard","tables","totalRows","s","t","DbStatsCard","data","loading","error","Fragment","ColumnAuditCard","columns","missing","c","SchemaHealthCard","line","i","ActionsCard","results","setResults","useState","running","setRunning","days","setDays","handleAction","useCallback","def","action","res","postMaintenanceAction","prev","err","isRunning","lastResult","r","e","MaintenanceTab","dbStats","statsLoading","statsError","useApi","fetchDbStats","schemaHealth","healthLoading","healthError","fetchSchemaHealth"],"mappings":"2EAuCA,MAAMA,EAAuB,CAC5B,CACC,IAAK,kBACL,MAAO,kBACP,KAAM,6BACN,UAAW,EAAA,EAEZ,CACC,IAAK,kBACL,MAAO,yBACP,KAAM,wCACN,UAAW,GACX,QAAS,2CAAA,EAEV,CACC,IAAK,aACL,MAAO,iBACP,KAAM,qCACN,UAAW,EAAA,EAEZ,CACC,IAAK,SACL,MAAO,SACP,KAAM,mCACN,UAAW,GACX,QAAS,0CAAA,EAEV,CACC,IAAK,UACL,MAAO,UACP,KAAM,sBACN,UAAW,GACX,QAAS,qCAAA,EAEV,CACC,IAAK,eACL,MAAO,eACP,KAAM,iCACN,UAAW,GACX,QAAS,uCAAA,EAEV,CACC,IAAK,QACL,MAAO,iBACP,KAAM,gDACN,UAAW,GACX,QAAS,GACT,QAAS,2CAAA,CAEX,EAMA,SAASC,EAASC,EAAmB,CACpC,OAAIA,EAAI,KAAa,GAAGA,CAAC,KACrBA,EAAI,KAAO,KAAa,IAAIA,EAAI,MAAM,QAAQ,CAAC,CAAC,MAC7C,IAAIA,GAAK,KAAO,OAAO,QAAQ,CAAC,CAAC,KACzC,CAMA,SAASC,EAAc,CACtB,MAAAC,EACA,SAAAC,EACA,UAAAC,EACA,cAAAC,CACD,EAKuB,CACtB,MAAMC,EAAQJ,EAAM,QAAUA,EAAM,SAAWA,EAAM,SACrD,OACCK,EAAAA,KAAC,MAAA,CAAI,UAAU,eACd,SAAA,CAAAC,EAAAA,IAAC,MAAA,CAAI,UAAU,aAAa,SAAA,aAAU,EACtCA,MAAC,QAAA,CAAM,UAAU,gBAChB,gBAAC,QAAA,CACA,SAAA,CAAAD,OAAC,KAAA,CACA,SAAA,CAAAC,EAAAA,IAAC,MAAG,SAAA,WAAA,CAAS,QACZ,KAAA,CAAG,UAAU,MAAO,SAAAT,EAASG,EAAM,OAAO,CAAA,CAAE,CAAA,EAC9C,EACCA,EAAM,SAAW,GACjBK,EAAAA,KAAC,KAAA,CACA,SAAA,CAAAC,EAAAA,IAAC,MAAG,SAAA,MAAA,CAAI,QACP,KAAA,CAAG,UAAU,MAAO,SAAAT,EAASG,EAAM,QAAQ,CAAA,CAAE,CAAA,EAC/C,EAEAA,EAAM,SAAW,GACjBK,EAAAA,KAAC,KAAA,CACA,SAAA,CAAAC,EAAAA,IAAC,MAAG,SAAA,MAAA,CAAI,QACP,KAAA,CAAG,UAAU,MAAO,SAAAT,EAASG,EAAM,QAAQ,CAAA,CAAE,CAAA,EAC/C,SAEA,KAAA,CACA,SAAA,CAAAM,EAAAA,IAAC,MAAG,SAAA,OAAA,CAAK,QACR,KAAA,CAAG,UAAU,MAAO,SAAAT,EAASO,CAAK,CAAA,CAAE,CAAA,EACtC,SACC,KAAA,CACA,SAAA,CAAAE,EAAAA,IAAC,MAAG,SAAA,WAAA,CAAS,QACZ,KAAA,CAAG,UAAU,MAAO,SAAAT,EAASI,CAAQ,CAAA,CAAE,CAAA,EACzC,SACC,KAAA,CACA,SAAA,CAAAK,EAAAA,IAAC,MAAG,SAAA,OAAA,CAAK,EACTD,EAAAA,KAAC,KAAA,CAAG,UAAU,MACZ,SAAA,CAAAH,EAAU,eAAA,EAAiB,KAAGC,EAAc,eAAA,EAAkB,IAAI,OAAA,CAAA,CAEpE,CAAA,CAAA,CACD,CAAA,CAAA,CACD,CAAA,CACD,CAAA,EACD,CAEF,CAEA,SAASI,EAAmB,CAC3B,OAAAC,CACD,EAEuB,CACtB,MAAMC,EAAYD,EAAO,OAAO,CAACE,EAAGC,IAAMD,EAAI,KAAK,IAAI,EAAGC,EAAE,QAAQ,EAAG,CAAC,EACxE,OACCN,EAAAA,KAAC,MAAA,CAAI,UAAU,eACd,SAAA,CAAAA,EAAAA,KAAC,MAAA,CAAI,UAAU,aAAa,SAAA,CAAA,qBACRI,EAAU,eAAA,EAAiB,SAAA,EAC/C,EACAJ,EAAAA,KAAC,QAAA,CAAM,UAAU,gBAChB,SAAA,CAAAC,EAAAA,IAAC,QAAA,CACA,gBAAC,KAAA,CACA,SAAA,CAAAA,EAAAA,IAAC,MAAG,SAAA,OAAA,CAAK,EACTA,EAAAA,IAAC,KAAA,CAAG,UAAU,MAAM,SAAA,MAAA,CAAI,CAAA,CAAA,CACzB,CAAA,CACD,QACC,QAAA,CACC,SAAAE,EAAO,IAAKG,UACX,KAAA,CACA,SAAA,CAAAL,EAAAA,IAAC,KAAA,CAAI,WAAE,KAAA,CAAM,EACbA,EAAAA,IAAC,KAAA,CAAG,UAAU,MACZ,SAAAK,EAAE,UAAY,EAAIA,EAAE,SAAS,eAAA,EAAmB,GAAA,CAClD,CAAA,GAJQA,EAAE,KAKX,CACA,CAAA,CACF,CAAA,CAAA,CACD,CAAA,EACD,CAEF,CAEA,SAASC,EAAY,CACpB,KAAAC,EACA,QAAAC,EACA,MAAAC,CACD,EAIuB,CACtB,OACCV,EAAAA,KAAC,MAAA,CAAI,UAAU,OACd,SAAA,CAAAC,EAAAA,IAAC,MAAA,CAAI,UAAU,cAAc,SAAA,WAAQ,EACrCD,EAAAA,KAAC,MAAA,CAAI,UAAU,YACb,SAAA,CAAAS,GAAW,CAACD,GAAQP,EAAAA,IAAC,OAAA,CAAK,UAAU,aAAa,SAAA,WAAQ,EACzDS,GAAS,CAACF,GAAQP,EAAAA,IAAC,QAAK,UAAU,aAAc,WAAM,QAAQ,EAC9DO,GACAR,EAAAA,KAAAW,WAAA,CACC,SAAA,CAAAV,EAAAA,IAACP,EAAA,CACA,MAAOc,EAAK,QAAQ,MACpB,SAAUA,EAAK,QAAQ,SACvB,UAAWA,EAAK,QAAQ,UACxB,cAAeA,EAAK,QAAQ,aAAA,CAAA,EAE7BP,EAAAA,IAACC,EAAA,CAAmB,OAAQM,EAAK,MAAA,CAAQ,CAAA,CAAA,CAC1C,CAAA,CAAA,CAEF,CAAA,EACD,CAEF,CAMA,SAASI,EAAgB,CACxB,QAAAC,CACD,EAEuB,CACtB,MAAMC,EAAUD,EAAQ,OAAQE,GAAM,CAACA,EAAE,OAAO,EAChD,OACCf,EAAAA,KAAC,MAAA,CAAI,UAAU,eACd,SAAA,CAAAA,EAAAA,KAAC,MAAA,CAAI,UAAU,aAAa,SAAA,CAAA,iBACZa,EAAQ,OAAO,aAAWC,EAAQ,OAAO,WAAA,EACzD,EACAd,EAAAA,KAAC,QAAA,CAAM,UAAU,gBAChB,SAAA,CAAAC,EAAAA,IAAC,QAAA,CACA,gBAAC,KAAA,CACA,SAAA,CAAAA,EAAAA,IAAC,MAAG,SAAA,OAAA,CAAK,EACTA,EAAAA,IAAC,MAAG,SAAA,QAAA,CAAM,EACVA,EAAAA,IAAC,MAAG,SAAA,MAAA,CAAI,EACRA,EAAAA,IAAC,MAAG,SAAA,QAAA,CAAM,CAAA,CAAA,CACX,CAAA,CACD,QACC,QAAA,CACC,SAAAY,EAAQ,IAAKE,UACZ,KAAA,CACA,SAAA,CAAAd,EAAAA,IAAC,KAAA,CAAI,WAAE,KAAA,CAAM,EACbA,EAAAA,IAAC,KAAA,CAAI,SAAAc,EAAE,MAAA,CAAO,EACdd,EAAAA,IAAC,KAAA,CAAG,UAAU,YAAa,WAAE,aAAa,EAC1CA,EAAAA,IAAC,KAAA,CAAG,UAAWc,EAAE,QAAU,UAAY,aACrC,SAAAA,EAAE,QAAU,IAAM,SAAA,CACpB,CAAA,GANQ,GAAGA,EAAE,KAAK,IAAIA,EAAE,MAAM,EAO/B,CACA,CAAA,CACF,CAAA,CAAA,CACD,CAAA,EACD,CAEF,CAEA,SAASC,EAAiB,CACzB,KAAAR,EACA,QAAAC,EACA,MAAAC,CACD,EAIuB,CACtB,OACCV,EAAAA,KAAC,MAAA,CAAI,UAAU,OACd,SAAA,CAAAA,EAAAA,KAAC,MAAA,CAAI,UAAU,cAAc,SAAA,CAAA,gBAE3BQ,GACAP,EAAAA,IAAC,OAAA,CAAK,UAAW,SAASO,EAAK,QAAU,WAAa,WAAW,GAC/D,SAAAA,EAAK,QAAU,UAAY,UAAA,CAC7B,CAAA,EAEF,EACAR,EAAAA,KAAC,MAAA,CAAI,UAAU,YACb,SAAA,CAAAS,GAAW,CAACD,GAAQP,EAAAA,IAAC,OAAA,CAAK,UAAU,aAAa,SAAA,WAAQ,EACzDS,GAAS,CAACF,GAAQP,EAAAA,IAAC,QAAK,UAAU,aAAc,WAAM,QAAQ,EAC9DO,GACAR,EAAAA,KAAAW,WAAA,CACC,SAAA,CAAAX,EAAAA,KAAC,MAAA,CAAI,UAAU,WACd,SAAA,CAAAC,EAAAA,IAAC,OAAA,CAAK,UAAU,aAAa,SAAA,iBAAc,EAC3CA,EAAAA,IAAC,OAAA,CAAK,UAAU,MAAO,WAAK,aAAA,CAAc,CAAA,EAC3C,EACAD,EAAAA,KAAC,MAAA,CAAI,UAAU,WACd,SAAA,CAAAC,EAAAA,IAAC,OAAA,CAAK,UAAU,aAAa,SAAA,YAAS,EACtCA,EAAAA,IAAC,OAAA,CACA,UACCO,EAAK,UAAU,CAAC,IAAM,KAAO,UAAY,aAGzC,SAAAA,EAAK,UAAU,KAAK,IAAI,CAAA,CAAA,CAC1B,EACD,EACAP,EAAAA,IAACW,EAAA,CAAgB,QAASJ,EAAK,OAAA,CAAS,EACvCA,EAAK,QAAQ,OAAS,GACtBR,EAAAA,KAAC,MAAA,CAAI,UAAU,eACd,SAAA,CAAAA,EAAAA,KAAC,MAAA,CAAI,UAAU,uBAAuB,SAAA,CAAA,aAC1BQ,EAAK,QAAQ,OAAO,GAAA,EAChC,EACCA,EAAK,QAAQ,IAAI,CAACS,EAAMC,IACxBjB,EAAAA,IAAC,MAAA,CAAY,UAAU,sBACrB,SAAAgB,CAAA,EADQC,CAEV,CACA,CAAA,CAAA,CACF,CAAA,CAAA,CAEF,CAAA,CAAA,CAEF,CAAA,EACD,CAEF,CAMA,SAASC,GAAkC,CAC1C,KAAM,CAACC,EAASC,CAAU,EAAIC,EAAAA,SAAoC,CAAA,CAAE,EAC9D,CAACC,EAASC,CAAU,EAAIF,EAAAA,SAAwB,IAAI,EACpD,CAACG,EAAMC,CAAO,EAAIJ,EAAAA,SAAS,IAAI,EAE/BK,EAAeC,EAAAA,YACpB,MAAOC,GAAmB,CACzB,MAAMC,EAA4BD,EAAI,QACnC,CAAE,OAAQ,QAAS,QAAS,OAAOJ,CAAI,CAAA,EACtC,CAAE,OAAQI,EAAI,GAAA,EAElBL,EAAWK,EAAI,GAAG,EAClB,GAAI,CACH,MAAME,EAAM,MAAMC,EAAsBF,CAAM,EAC9CT,EAAYY,GACX,CAAC,CAAE,GAAGF,EAAK,UAAW,KAAK,IAAA,CAAI,EAAK,GAAGE,CAAI,EAAE,MAAM,EAAG,EAAE,CAAA,CAE1D,OAASC,EAAK,CACbb,EAAYY,GACX,CACC,CACC,UAAWJ,EAAI,IACf,QAAS,GACT,SAAU,EACV,eAAgB,EAChB,QAASK,aAAe,MAAQA,EAAI,QAAU,OAAOA,CAAG,EACxD,UAAW,KAAK,IAAA,CAAI,EAErB,GAAGD,CAAA,EACF,MAAM,EAAG,EAAE,CAAA,CAEf,QAAA,CACCT,EAAW,IAAI,CAChB,CACD,EACA,CAACC,CAAI,CAAA,EAGN,OACCzB,EAAAA,KAAC,MAAA,CAAI,UAAU,OACd,SAAA,CAAAC,EAAAA,IAAC,MAAA,CAAI,UAAU,cAAc,SAAA,UAAO,EACpCA,EAAAA,IAAC,MAAA,CAAI,UAAU,YACd,SAAAA,EAAAA,IAAC,MAAA,CAAI,UAAU,cACb,SAAAV,EAAQ,IAAKsC,GAAQ,CACrB,MAAMM,EAAYZ,IAAYM,EAAI,IAC5BO,EAAahB,EAAQ,KAAMiB,GAAMA,EAAE,YAAcR,EAAI,GAAG,EAC9D,OACC7B,EAAAA,KAAC,MAAA,CAAkB,UAAU,cAC5B,SAAA,CAAAA,EAAAA,KAAC,MAAA,CAAI,UAAU,cACd,SAAA,CAAAA,EAAAA,KAAC,MAAA,CAAI,UAAU,eACb,SAAA,CAAA6B,EAAI,MACJA,EAAI,WACJ5B,EAAAA,IAAC,OAAA,CAAK,UAAU,mBAAmB,SAAA,aAAA,CAAW,CAAA,EAEhD,EACAA,EAAAA,IAAC,MAAA,CAAI,UAAU,cAAe,WAAI,KAAK,EACtCmC,GACAnC,EAAAA,IAAC,MAAA,CACA,UAAW,iBAAiBmC,EAAW,QAAU,UAAY,YAAY,GAExE,SAAAA,EAAW,OAAA,CAAA,CACb,EAEF,EACApC,EAAAA,KAAC,MAAA,CAAI,UAAU,kBACb,SAAA,CAAA6B,EAAI,SACJ5B,EAAAA,IAAC,QAAA,CACA,KAAK,SACL,IAAK,EACL,IAAK,IACL,MAAOwB,EACP,SAAWa,GAAMZ,EAAQY,EAAE,OAAO,KAAK,EACvC,UAAU,aACV,SAAUH,CAAA,CAAA,EAGZlC,EAAAA,IAAC,SAAA,CACA,UAAW,OAAO4B,EAAI,UAAY,aAAe,aAAa,GAC9D,SAAUN,IAAY,KACtB,QAAS,IAAM,CACVM,EAAI,SAAW,CAAC,OAAO,QAAQA,EAAI,OAAO,GAC9CF,EAAaE,CAAG,CACjB,EAEC,WAAY,WAAa,KAAA,CAAA,CAC3B,CAAA,CACD,CAAA,CAAA,EAvCSA,EAAI,GAwCd,CAEF,CAAC,EACF,CAAA,CACD,CAAA,EACD,CAEF,CAMA,SAAwBU,GAAqC,CAC5D,KAAM,CACL,KAAMC,EACN,QAASC,EACT,MAAOC,CAAA,EACJC,EACHf,cAAY,IAAMgB,EAAA,EAAgB,EAAE,EACpC,CAAE,aAAc,IAAA,CAAM,EAGjB,CACL,KAAMC,EACN,QAASC,EACT,MAAOC,CAAA,EACJJ,EACHf,cAAY,IAAMoB,EAAA,EAAqB,EAAE,EACzC,CAAE,aAAc,GAAA,CAAM,EAGvB,OACChD,EAAAA,KAAC,MAAA,CAAI,UAAU,kBACd,SAAA,CAAAA,EAAAA,KAAC,MAAA,CAAI,UAAU,YACd,SAAA,CAAAC,EAAAA,IAACe,EAAA,CACA,KAAM6B,EACN,QAASC,EACT,MAAOC,CAAA,CAAA,QAEPxC,EAAA,CAAY,KAAMiC,EAAS,QAASC,EAAc,MAAOC,CAAA,CAAY,CAAA,EACvE,QACCvB,EAAA,CAAA,CAAY,CAAA,EACd,CAEF"}
|
|
1
|
+
{"version":3,"file":"MaintenanceTab-LblMg3NB.js","sources":["../../src/tabs/MaintenanceTab.tsx"],"sourcesContent":["/**\n * dashboard-client/src/tabs/MaintenanceTab.tsx — Maintenance tab (v0.11.2 S49B).\n *\n * Three cards:\n * 1. Schema Health (schema version + integrity + column audit)\n * 2. DB Stats (file sizes + table row counts)\n * 3. Actions (one-time maintenance operations with results history)\n *\n * All three fetch independently; failures in one do not block the others.\n */\n\nimport type React from \"react\";\nimport { useState, useCallback } from \"react\";\nimport type {\n\tDbStatsResponse,\n\tSchemaHealthResponse,\n\tMaintenanceAction,\n\tMaintenanceActionResult,\n} from \"@contracts\";\nimport { useApi } from \"../hooks/useApi\";\nimport {\n\tfetchDbStats,\n\tfetchSchemaHealth,\n\tpostMaintenanceAction,\n} from \"../api/client\";\n\n// ---------------------------------------------------------------------------\n// Action config\n// ---------------------------------------------------------------------------\n\ninterface ActionDef {\n\tkey: string;\n\tlabel: string;\n\tdesc: string;\n\tdangerous: boolean;\n\tconfirm?: string;\n\tdaysOld?: boolean;\n}\n\nconst ACTIONS: ActionDef[] = [\n\t{\n\t\tkey: \"integrity-check\",\n\t\tlabel: \"Integrity Check\",\n\t\tdesc: \"Run PRAGMA integrity_check\",\n\t\tdangerous: false,\n\t},\n\t{\n\t\tkey: \"reconcile-dedup\",\n\t\tlabel: \"Reconcile Dedup Mirror\",\n\t\tdesc: \"Fix orphan dedup rows + backfill refs\",\n\t\tdangerous: false,\n\t\tconfirm: \"Reconcile dedup_mirror (safe read/write)?\",\n\t},\n\t{\n\t\tkey: \"checkpoint\",\n\t\tlabel: \"WAL Checkpoint\",\n\t\tdesc: \"Merge WAL into main DB (read-only)\",\n\t\tdangerous: false,\n\t},\n\t{\n\t\tkey: \"vacuum\",\n\t\tlabel: \"VACUUM\",\n\t\tdesc: \"Rebuild DB reclaiming free pages\",\n\t\tdangerous: true,\n\t\tconfirm: \"VACUUM rewrites the entire DB. Continue?\",\n\t},\n\t{\n\t\tkey: \"reindex\",\n\t\tlabel: \"REINDEX\",\n\t\tdesc: \"Rebuild all indexes\",\n\t\tdangerous: true,\n\t\tconfirm: \"REINDEX rebuilds indexes. Continue?\",\n\t},\n\t{\n\t\tkey: \"fts5-rebuild\",\n\t\tlabel: \"FTS5 Rebuild\",\n\t\tdesc: \"Rebuild full-text search index\",\n\t\tdangerous: true,\n\t\tconfirm: \"Rebuild FTS5 trigram index. Continue?\",\n\t},\n\t{\n\t\tkey: \"prune\",\n\t\tlabel: \"Prune Old Rows\",\n\t\tdesc: \"Delete transcripts + epochs older than N days\",\n\t\tdangerous: true,\n\t\tdaysOld: true,\n\t\tconfirm: \"Delete old raw_transcript rows. Continue?\",\n\t},\n];\n\n// ---------------------------------------------------------------------------\n// Format helpers (local — matches html.ts fmtBytes)\n// ---------------------------------------------------------------------------\n\nfunction fmtBytes(b: number): string {\n\tif (b < 1024) return `${b} B`;\n\tif (b < 1024 * 1024) return `${(b / 1024).toFixed(1)} KB`;\n\treturn `${(b / (1024 * 1024)).toFixed(1)} MB`;\n}\n\n// ---------------------------------------------------------------------------\n// DB Stats card\n// ---------------------------------------------------------------------------\n\nfunction FileSizesCard({\n\tfiles,\n\tpageSize,\n\tpageCount,\n\tfreelistPages,\n}: {\n\tfiles: DbStatsResponse[\"storage\"][\"files\"];\n\tpageSize: number;\n\tpageCount: number;\n\tfreelistPages: number;\n}): React.ReactElement {\n\tconst total = files.dbBytes + files.walBytes + files.shmBytes;\n\treturn (\n\t\t<div className=\"stat-section\">\n\t\t\t<div className=\"stat-label\">File Sizes</div>\n\t\t\t<table className=\"compact-table\">\n\t\t\t\t<tbody>\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<td>sqlite.db</td>\n\t\t\t\t\t\t<td className=\"num\">{fmtBytes(files.dbBytes)}</td>\n\t\t\t\t\t</tr>\n\t\t\t\t\t{files.walBytes > 0 && (\n\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t<td>.wal</td>\n\t\t\t\t\t\t\t<td className=\"num\">{fmtBytes(files.walBytes)}</td>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t)}\n\t\t\t\t\t{files.shmBytes > 0 && (\n\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t<td>.shm</td>\n\t\t\t\t\t\t\t<td className=\"num\">{fmtBytes(files.shmBytes)}</td>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t)}\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<td>Total</td>\n\t\t\t\t\t\t<td className=\"num\">{fmtBytes(total)}</td>\n\t\t\t\t\t</tr>\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<td>Page size</td>\n\t\t\t\t\t\t<td className=\"num\">{fmtBytes(pageSize)}</td>\n\t\t\t\t\t</tr>\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<td>Pages</td>\n\t\t\t\t\t\t<td className=\"num\">\n\t\t\t\t\t\t\t{pageCount.toLocaleString()} ({freelistPages.toLocaleString()}{\" \"}\n\t\t\t\t\t\t\tfree)\n\t\t\t\t\t\t</td>\n\t\t\t\t\t</tr>\n\t\t\t\t</tbody>\n\t\t\t</table>\n\t\t</div>\n\t);\n}\n\nfunction TableRowCountsCard({\n\ttables,\n}: {\n\ttables: DbStatsResponse[\"tables\"];\n}): React.ReactElement {\n\tconst totalRows = tables.reduce((s, t) => s + Math.max(0, t.rowCount), 0);\n\treturn (\n\t\t<div className=\"stat-section\">\n\t\t\t<div className=\"stat-label\">\n\t\t\t\tTable Row Counts ({totalRows.toLocaleString()} total)\n\t\t\t</div>\n\t\t\t<table className=\"compact-table\">\n\t\t\t\t<thead>\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<th>Table</th>\n\t\t\t\t\t\t<th className=\"num\">Rows</th>\n\t\t\t\t\t</tr>\n\t\t\t\t</thead>\n\t\t\t\t<tbody>\n\t\t\t\t\t{tables.map((t) => (\n\t\t\t\t\t\t<tr key={t.table}>\n\t\t\t\t\t\t\t<td>{t.table}</td>\n\t\t\t\t\t\t\t<td className=\"num\">\n\t\t\t\t\t\t\t\t{t.rowCount >= 0 ? t.rowCount.toLocaleString() : \"—\"}\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t))}\n\t\t\t\t</tbody>\n\t\t\t</table>\n\t\t</div>\n\t);\n}\n\nfunction DbStatsCard({\n\tdata,\n\tloading,\n\terror,\n}: {\n\tdata: DbStatsResponse | null;\n\tloading: boolean;\n\terror: Error | null;\n}): React.ReactElement {\n\treturn (\n\t\t<div className=\"card\">\n\t\t\t<div className=\"card-header\">DB Stats</div>\n\t\t\t<div className=\"card-body\">\n\t\t\t\t{loading && !data && <span className=\"text-muted\">Loading…</span>}\n\t\t\t\t{error && !data && <span className=\"text-error\">{error.message}</span>}\n\t\t\t\t{data && (\n\t\t\t\t\t<>\n\t\t\t\t\t\t<FileSizesCard\n\t\t\t\t\t\t\tfiles={data.storage.files}\n\t\t\t\t\t\t\tpageSize={data.storage.pageSize}\n\t\t\t\t\t\t\tpageCount={data.storage.pageCount}\n\t\t\t\t\t\t\tfreelistPages={data.storage.freelistPages}\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t<TableRowCountsCard tables={data.tables} />\n\t\t\t\t\t</>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\n// ---------------------------------------------------------------------------\n// Schema Health card\n// ---------------------------------------------------------------------------\n\nfunction ColumnAuditCard({\n\tcolumns,\n}: {\n\tcolumns: SchemaHealthResponse[\"columns\"];\n}): React.ReactElement {\n\tconst missing = columns.filter((c) => !c.present);\n\treturn (\n\t\t<div className=\"stat-section\">\n\t\t\t<div className=\"stat-label\">\n\t\t\t\tColumn Audit ({columns.length} columns, {missing.length} missing)\n\t\t\t</div>\n\t\t\t<table className=\"compact-table\">\n\t\t\t\t<thead>\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<th>Table</th>\n\t\t\t\t\t\t<th>Column</th>\n\t\t\t\t\t\t<th>Decl</th>\n\t\t\t\t\t\t<th>Status</th>\n\t\t\t\t\t</tr>\n\t\t\t\t</thead>\n\t\t\t\t<tbody>\n\t\t\t\t\t{columns.map((c) => (\n\t\t\t\t\t\t<tr key={`${c.table}.${c.column}`}>\n\t\t\t\t\t\t\t<td>{c.table}</td>\n\t\t\t\t\t\t\t<td>{c.column}</td>\n\t\t\t\t\t\t\t<td className=\"text-mono\">{c.expectedDecl}</td>\n\t\t\t\t\t\t\t<td className={c.present ? \"text-ok\" : \"text-error\"}>\n\t\t\t\t\t\t\t\t{c.present ? \"✓\" : \"MISSING\"}\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t))}\n\t\t\t\t</tbody>\n\t\t\t</table>\n\t\t</div>\n\t);\n}\n\nfunction SchemaHealthCard({\n\tdata,\n\tloading,\n\terror,\n}: {\n\tdata: SchemaHealthResponse | null;\n\tloading: boolean;\n\terror: Error | null;\n}): React.ReactElement {\n\treturn (\n\t\t<div className=\"card\">\n\t\t\t<div className=\"card-header\">\n\t\t\t\tSchema Health\n\t\t\t\t{data && (\n\t\t\t\t\t<span className={`badge ${data.healthy ? \"badge-ok\" : \"badge-bad\"}`}>\n\t\t\t\t\t\t{data.healthy ? \"HEALTHY\" : \"DEGRADED\"}\n\t\t\t\t\t</span>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t\t<div className=\"card-body\">\n\t\t\t\t{loading && !data && <span className=\"text-muted\">Loading…</span>}\n\t\t\t\t{error && !data && <span className=\"text-error\">{error.message}</span>}\n\t\t\t\t{data && (\n\t\t\t\t\t<>\n\t\t\t\t\t\t<div className=\"stat-row\">\n\t\t\t\t\t\t\t<span className=\"stat-label\">Schema version</span>\n\t\t\t\t\t\t\t<span className=\"num\">{data.schemaVersion}</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div className=\"stat-row\">\n\t\t\t\t\t\t\t<span className=\"stat-label\">Integrity</span>\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tclassName={\n\t\t\t\t\t\t\t\t\tdata.integrity[0] === \"ok\" ? \"text-ok\" : \"text-error\"\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{data.integrity.join(\"; \")}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<ColumnAuditCard columns={data.columns} />\n\t\t\t\t\t\t{data.fkCheck.length > 0 && (\n\t\t\t\t\t\t\t<div className=\"stat-section\">\n\t\t\t\t\t\t\t\t<div className=\"stat-label text-warn\">\n\t\t\t\t\t\t\t\t\tFK Check ({data.fkCheck.length})\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t{data.fkCheck.map((line, i) => (\n\t\t\t\t\t\t\t\t\t<div key={i} className=\"text-warn text-mono\">\n\t\t\t\t\t\t\t\t\t\t{line}\n\t\t\t\t\t\t\t\t\t</div>\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\t\t\t\t\t</>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\n// ---------------------------------------------------------------------------\n// Actions card\n// ---------------------------------------------------------------------------\n\nfunction ActionsCard(): React.ReactElement {\n\tconst [results, setResults] = useState<MaintenanceActionResult[]>([]);\n\tconst [running, setRunning] = useState<string | null>(null);\n\tconst [days, setDays] = useState(\"30\");\n\n\tconst handleAction = useCallback(\n\t\tasync (def: ActionDef) => {\n\t\t\tconst action: MaintenanceAction = def.daysOld\n\t\t\t\t? { action: \"prune\", daysOld: Number(days) }\n\t\t\t\t: ({ action: def.key } as MaintenanceAction);\n\n\t\t\tsetRunning(def.key);\n\t\t\ttry {\n\t\t\t\tconst res = await postMaintenanceAction(action);\n\t\t\t\tsetResults((prev) =>\n\t\t\t\t\t[{ ...res, timestamp: Date.now() }, ...prev].slice(0, 20),\n\t\t\t\t);\n\t\t\t} catch (err) {\n\t\t\t\tsetResults((prev) =>\n\t\t\t\t\t[\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\toperation: def.key,\n\t\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\t\taffected: 0,\n\t\t\t\t\t\t\treclaimedBytes: 0,\n\t\t\t\t\t\t\tsummary: err instanceof Error ? err.message : String(err),\n\t\t\t\t\t\t\ttimestamp: Date.now(),\n\t\t\t\t\t\t},\n\t\t\t\t\t\t...prev,\n\t\t\t\t\t].slice(0, 20),\n\t\t\t\t);\n\t\t\t} finally {\n\t\t\t\tsetRunning(null);\n\t\t\t}\n\t\t},\n\t\t[days],\n\t);\n\n\treturn (\n\t\t<div className=\"card\">\n\t\t\t<div className=\"card-header\">Actions</div>\n\t\t\t<div className=\"card-body\">\n\t\t\t\t<div className=\"action-grid\">\n\t\t\t\t\t{ACTIONS.map((def) => {\n\t\t\t\t\t\tconst isRunning = running === def.key;\n\t\t\t\t\t\tconst lastResult = results.find((r) => r.operation === def.key);\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t<div key={def.key} className=\"action-item\">\n\t\t\t\t\t\t\t\t<div className=\"action-info\">\n\t\t\t\t\t\t\t\t\t<div className=\"action-label\">\n\t\t\t\t\t\t\t\t\t\t{def.label}\n\t\t\t\t\t\t\t\t\t\t{def.dangerous && (\n\t\t\t\t\t\t\t\t\t\t\t<span className=\"badge badge-warn\">DESTRUCTIVE</span>\n\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t<div className=\"action-desc\">{def.desc}</div>\n\t\t\t\t\t\t\t\t\t{lastResult && (\n\t\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\t\tclassName={`action-result ${lastResult.success ? \"text-ok\" : \"text-error\"}`}\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t{lastResult.summary}\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t<div className=\"action-controls\">\n\t\t\t\t\t\t\t\t\t{def.daysOld && (\n\t\t\t\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\t\t\t\ttype=\"number\"\n\t\t\t\t\t\t\t\t\t\t\tmin={1}\n\t\t\t\t\t\t\t\t\t\t\tmax={365}\n\t\t\t\t\t\t\t\t\t\t\tvalue={days}\n\t\t\t\t\t\t\t\t\t\t\tonChange={(e) => setDays(e.target.value)}\n\t\t\t\t\t\t\t\t\t\t\tclassName=\"days-input\"\n\t\t\t\t\t\t\t\t\t\t\tdisabled={isRunning}\n\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\tclassName={`btn ${def.dangerous ? \"btn-danger\" : \"btn-primary\"}`}\n\t\t\t\t\t\t\t\t\t\tdisabled={running !== null}\n\t\t\t\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\t\t\t\tif (def.confirm && !window.confirm(def.confirm)) return;\n\t\t\t\t\t\t\t\t\t\t\thandleAction(def);\n\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{isRunning ? \"Running…\" : \"Run\"}\n\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t);\n\t\t\t\t\t})}\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\n// ---------------------------------------------------------------------------\n// Main tab component\n// ---------------------------------------------------------------------------\n\nexport default function MaintenanceTab(): React.ReactElement {\n\tconst {\n\t\tdata: dbStats,\n\t\tloading: statsLoading,\n\t\terror: statsError,\n\t} = useApi<DbStatsResponse>(\n\t\tuseCallback(() => fetchDbStats(), []),\n\t\t{ pollInterval: 15000 },\n\t);\n\n\tconst {\n\t\tdata: schemaHealth,\n\t\tloading: healthLoading,\n\t\terror: healthError,\n\t} = useApi<SchemaHealthResponse>(\n\t\tuseCallback(() => fetchSchemaHealth(), []),\n\t\t{ pollInterval: 10000 },\n\t);\n\n\treturn (\n\t\t<div className=\"maintenance-tab\">\n\t\t\t<div className=\"card-grid\">\n\t\t\t\t<SchemaHealthCard\n\t\t\t\t\tdata={schemaHealth}\n\t\t\t\t\tloading={healthLoading}\n\t\t\t\t\terror={healthError}\n\t\t\t\t/>\n\t\t\t\t<DbStatsCard data={dbStats} loading={statsLoading} error={statsError} />\n\t\t\t</div>\n\t\t\t<ActionsCard />\n\t\t</div>\n\t);\n}\n"],"names":["ACTIONS","fmtBytes","b","FileSizesCard","files","pageSize","pageCount","freelistPages","total","jsxs","jsx","TableRowCountsCard","tables","totalRows","s","t","DbStatsCard","data","loading","error","Fragment","ColumnAuditCard","columns","missing","c","SchemaHealthCard","line","i","ActionsCard","results","setResults","useState","running","setRunning","days","setDays","handleAction","useCallback","def","action","res","postMaintenanceAction","prev","err","isRunning","lastResult","r","e","MaintenanceTab","dbStats","statsLoading","statsError","useApi","fetchDbStats","schemaHealth","healthLoading","healthError","fetchSchemaHealth"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{j as s,r as p}from"./index-PRkilPXw.js";const S={checkpoint:"#6366f1",turn:"#58a6ff","turn-content":"#22c55e",memory:"#f59e0b"},q={checkpoint:"Checkpoint",turn:"Turn","turn-content":"Turn Content",memory:"Memory"};function V(o,n){return`M 0,-${n} L ${o},0 L 0,${n} L -${o},0 Z`}function z(o){return!o||o==="active"?1:.5}function B(o,n,c,m,t,d,l){const h=d?S[m]??"#6366f1":"#d1d5db",i=t?"#ef4444":"none",u=z(l);switch(m){case"checkpoint":return s.jsx("circle",{cx:o,cy:n,r:t?c+3:c,fill:h,opacity:u,stroke:i,strokeWidth:t?2:0});case"turn":return s.jsx("circle",{cx:o,cy:n,r:t?c+3:c,fill:"none",stroke:h,strokeWidth:2.5,opacity:u});case"turn-content":return s.jsxs("g",{children:[s.jsx("circle",{cx:o,cy:n,r:t?c+3:c,fill:"none",stroke:h,strokeWidth:2.5,opacity:u}),s.jsx("circle",{cx:o,cy:n,r:Math.max(3,c*.3),fill:h,opacity:u})]});case"memory":const x=c*.85,j=c*.85;return s.jsx("path",{d:V(x,j),transform:`translate(${o}, ${n})`,fill:h,opacity:u,stroke:i,strokeWidth:t?2:0})}}const U=5e3,A=.005,P=.01,_=.9,Y=.1,$=50;function Q(o){const n=o.nodes.map((t,d)=>{const l=2*Math.PI*d/Math.max(o.nodes.length,1),h=200;return{...t,pos:{x:Math.cos(l)*h+300,y:Math.sin(l)*h+200},vx:0,vy:0,pinned:!1}}),c=new Map;n.forEach((t,d)=>c.set(t.id,d));const m=o.edges.map(t=>{const d=c.get(t.source),l=c.get(t.target);return d===void 0||l===void 0||d===l?null:{source:d,target:l,weight:t.weight,type:t.type}}).filter(t=>t!==null);return{nodes:n,edges:m}}function X(o){const{nodes:n,edges:c}=o,m=n.length;if(m!==0)for(let t=0;t<m;t++){if(n[t].pinned)continue;let d=0,l=0;for(let i=0;i<m;i++){if(t===i)continue;const u=n[t].pos.x-n[i].pos.x,x=n[t].pos.y-n[i].pos.y,j=u*u+x*x||1,b=U/j,g=Math.sqrt(j);d+=u/g*b,l+=x/g*b}for(const i of c){let u;if(i.source===t)u=i.target;else if(i.target===t)u=i.source;else continue;const x=n[u].pos.x-n[t].pos.x,j=n[u].pos.y-n[t].pos.y;d+=x*A*i.weight,l+=j*A*i.weight}d-=n[t].pos.x*P,l-=n[t].pos.y*P,n[t].vx=(n[t].vx+d)*_,n[t].vy=(n[t].vy+l)*_;const h=Math.sqrt(n[t].vx*n[t].vx+n[t].vy*n[t].vy);h>$&&(n[t].vx=n[t].vx/h*$,n[t].vy=n[t].vy/h*$),h>Y&&(n[t].pos.x+=n[t].vx,n[t].pos.y+=n[t].vy)}}const Z=300,J=.3,k=900,w=600,y=40;function M(o){switch(o){case"temporal":return"#6366f1";case"semantic":return"#22c55e";case"raptor_parent":return"#f59e0b"}}const N=({color:o,shape:n,label:c})=>{const m={display:"inline-block",width:"10px",height:"10px",verticalAlign:"middle",marginRight:"3px",borderRadius:n.startsWith("hollow")||n==="filled-circle"?"50%":void 0};let t;return n==="line"?s.jsx("span",{style:{color:o,marginRight:"6px"},children:c}):(n==="filled-circle"?t=s.jsx("span",{style:{...m,backgroundColor:o}}):n==="hollow-circle"?t=s.jsx("span",{style:{...m,border:`2px solid ${o}`,backgroundColor:"transparent"}}):n==="hollow-ring"?t=s.jsx("span",{style:{...m,border:`2px solid ${o}`,backgroundColor:"transparent",position:"relative"},children:s.jsx("span",{style:{display:"block",width:"4px",height:"4px",borderRadius:"50%",backgroundColor:o,margin:"2px auto"}})}):t=s.jsx("span",{style:{...m,clipPath:"polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%)",backgroundColor:o}}),s.jsxs(s.Fragment,{children:[s.jsxs("span",{className:"memory-map-legend-item",children:[t,c]})," "]}))},ee=()=>{const[o,n]=p.useState(null),[c,m]=p.useState(!0),[t,d]=p.useState(null),[l,h]=p.useState(null),[i,u]=p.useState(""),[x]=p.useState(""),[j,b]=p.useState(0),g=p.useRef(null),C=p.useRef(0);p.useEffect(()=>{let e=!1;const r=new URLSearchParams;return x&&r.set("sessionId",x),r.set("threshold","0.7"),r.set("maxEdgesPerNode","4"),m(!0),d(null),fetch(`/api/memory-map?${r.toString()}`).then(a=>{if(!a.ok)throw new Error(`HTTP ${a.status}`);return a.json()}).then(a=>{e||(n(a),g.current=Q(a),b(1),m(!1))}).catch(a=>{e||(d(a instanceof Error?a.message:"Unknown error"),m(!1))}),()=>{e=!0,cancelAnimationFrame(C.current)}},[x]),p.useEffect(()=>{if(!g.current||j===0)return;const e=g.current;let r=0;const a=()=>{r>=Z||(X(e),r++,b(r),C.current=requestAnimationFrame(a))};return C.current=requestAnimationFrame(a),()=>cancelAnimationFrame(C.current)},[j]);const W=p.useCallback((e,r)=>{const a=g.current;a&&(a.nodes[r].pinned=!a.nodes[r].pinned,a.nodes[r].pinned||(a.nodes[r].vx=0,a.nodes[r].vy=0),h(f=>f?.id===e.id?null:e),b(f=>f+1))},[]),L=p.useMemo(()=>{const e=g.current;if(!e||!i)return null;const r=i.toLowerCase();return new Set(e.nodes.map((a,f)=>a.summaryTruncated.toLowerCase().includes(r)||a.label.toLowerCase().includes(r)?f:-1).filter(a=>a>=0))},[i]),E=p.useMemo(()=>{const e=o?.validation;return e?e.gatesPassed<e.gatesRun?{level:"red",label:"Gate failure"}:e.warnings.length>0?{level:"yellow",label:`${e.warnings.length} warning(s)`}:{level:"green",label:"Healthy"}:{level:"yellow",label:"No validation"}},[o?.validation]),D={green:"#22c55e",yellow:"#eab308",red:"#ef4444"},F=p.useMemo(()=>{const e=o?.validation;if(!e)return`${o?.metadata.totalNodes??0} nodes`;const r=[];return e.sources.checkpoint>0&&r.push(`${e.sources.checkpoint} checkpoints`),e.sources.turn>0&&r.push(`${e.sources.turn} turns`),e.sources.turnContent>0&&r.push(`${e.sources.turnContent} turn-content`),e.sources.memory>0&&r.push(`${e.sources.memory} memories`),r.length===0&&r.push(`${o?.metadata.totalNodes??0} nodes`),r.join(" · ")},[o?.validation,o?.metadata.totalNodes]),I=p.useMemo(()=>{const e=o?.validation;if(!e)return"";const r=[];return r.push(e.sources.checkpoint>0?"✓ checkpoints":"✗ checkpoints"),r.push(e.sources.turn>0?"✓ turns":"✗ turns"),r.push(e.sources.turnContent>0?"✓ turn-content":"✗ turn-content"),r.push(e.sources.memory>0?"✓ memories":"✗ memories"),r.join(" | ")},[o?.validation]),O=p.useMemo(()=>{const e=o?.validation;return e?e.sources.checkpoint===0&&e.sources.turn===0&&e.sources.turnContent===0&&e.sources.memory===0:o?.metadata.totalNodes===0},[o?.validation,o?.metadata.totalNodes]);if(c)return s.jsx("div",{className:"memory-map-loading",children:"Loading memory graph..."});if(t)return s.jsxs("div",{className:"memory-map-error",children:[s.jsxs("p",{children:["Failed to load memory map: ",t]}),s.jsx("button",{className:"memory-map-retry-btn",onClick:()=>b(e=>e+1),children:"Retry"})]});if(!o||!g.current||O)return s.jsx("div",{className:"memory-map-empty",children:s.jsx("p",{children:"Memories appear after your first compaction. The graph shows checkpoints (compaction summaries) linked by semantic similarity and time. Run a longer session or lower the compaction tier to see it sooner."})});const v=g.current;return s.jsxs("div",{className:"memory-map-container",children:[s.jsxs("div",{className:"memory-map-toolbar",children:[s.jsx("input",{type:"text",placeholder:"Search memories...",value:i,onInput:e=>u(e.target.value),className:"memory-map-search"}),s.jsxs("span",{className:"memory-map-stats",children:[s.jsx("span",{className:"memory-map-count-badge",children:F}),s.jsx("br",{}),s.jsxs("span",{className:"memory-map-edges",children:[o.edges.length," edges"]})]}),s.jsx("span",{className:"memory-map-health-badge",style:{backgroundColor:D[E.level]??"#6b7280",color:"#fff",padding:"2px 8px",borderRadius:"10px",fontSize:"11px",fontWeight:600,marginLeft:"8px"},title:`Validation: ${E.label}`,children:E.level==="green"?"Healthy":E.level==="yellow"?"Warnings":"Critical"})]}),I?s.jsx("div",{className:"memory-map-source-avail",style:{fontSize:"11px",padding:"2px 12px",color:"#6b7280"},children:I}):null,s.jsxs("div",{className:"memory-map-legend",children:[s.jsx("span",{className:"memory-map-legend-section",children:"Nodes:"}),s.jsx(N,{type:"node",color:S.checkpoint,shape:"filled-circle",label:"Checkpoint"}),s.jsx(N,{type:"node",color:S.turn,shape:"hollow-circle",label:"Turn"}),s.jsx(N,{type:"node",color:S["turn-content"],shape:"hollow-ring",label:"Turn Content"}),s.jsx(N,{type:"node",color:S.memory,shape:"diamond",label:"Memory"}),s.jsx("span",{className:"memory-map-legend-sep",children:"|"}),s.jsx("span",{className:"memory-map-legend-section",children:"Edges:"}),s.jsx(N,{type:"edge",color:M("semantic"),shape:"line",label:"Semantic"}),s.jsx(N,{type:"edge",color:M("temporal"),shape:"line",label:"Temporal"}),s.jsx(N,{type:"edge",color:M("raptor_parent"),shape:"line",label:"Raptor"})]}),s.jsxs("svg",{width:k,height:w,viewBox:`0 0 ${k} ${w}`,className:"memory-map-svg",children:[v.edges.map((e,r)=>{const a=v.nodes[e.source],f=v.nodes[e.target],T=M(e.type),R=Math.max(1,e.weight*2.5);return s.jsx("line",{x1:y+a.pos.x/(v.nodes.length||1)*(k-2*y),y1:y+a.pos.y/(v.nodes.length||1)*(w-2*y),x2:y+f.pos.x/(v.nodes.length||1)*(k-2*y),y2:y+f.pos.y/(v.nodes.length||1)*(w-2*y),stroke:T,strokeWidth:R,opacity:J},`edge-${r}`)}),v.nodes.map((e,r)=>{const a=y+e.pos.x/(v.nodes.length||1)*(k-2*y),f=y+e.pos.y/(v.nodes.length||1)*(w-2*y),T=Math.min(20,8+e.tokenEstimate/500),R=L?L.has(r):!0,G=l?.id===e.id,H=e.label.length>20?e.label.slice(0,18)+"...":e.label;return s.jsxs("g",{className:"memory-map-node-group",onClick:()=>W(e,r),style:{cursor:"pointer"},children:[B(a,f,T,e.nodeType,G,R,e.dedupStatus),s.jsx("text",{x:a,y:f+T+12,textAnchor:"middle",fontSize:"9px",fill:"#374151",className:"memory-map-label",children:H})]},`node-${r}`)})]}),l?s.jsxs("div",{className:"memory-map-detail",children:[s.jsxs("h3",{children:[s.jsx("span",{className:"memory-map-detail-type",style:{display:"inline-block",fontSize:"12px",fontWeight:400,color:"#6b7280",marginRight:"6px"},children:q[l.nodeType]}),l.label]}),s.jsx("p",{children:l.summaryTruncated}),l.topicSummary?s.jsxs("p",{className:"memory-map-topic",children:["Topic: ",l.topicSummary]}):null,s.jsxs("p",{children:["Tokens: ",l.tokenEstimate," | Decisions: ",l.decisionCount]}),s.jsxs("p",{children:["Session: ",l.sessionId]}),s.jsxs("p",{children:["Raptor Level: ",l.raptorLevel]}),s.jsx("p",{className:"memory-map-snippet",children:l.textSnippet})]}):null]})};export{ee as default};
|
|
2
|
+
//# sourceMappingURL=MemoryMapTab-CpjMQwRr.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MemoryMapTab-CpjMQwRr.js","sources":["../../src/memory-map-shapes.tsx","../../src/memory-map-layout.ts","../../src/tabs/MemoryMapTab.tsx"],"sourcesContent":["/**\n * memory-map-shapes.tsx — Node shape rendering utilities for MemoryMapTab (D3).\n *\n * Node shape encodes nodeType: checkpoint = filled circle, turn = hollow circle,\n * turn-content = hollow+ring, memory = diamond.\n * Delegate-shell pattern: thin rendering helpers, no state, no side effects.\n */\nimport type React from \"react\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport type NodeType = \"checkpoint\" | \"turn\" | \"turn-content\" | \"memory\";\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nexport const NODE_COLORS: Record<NodeType, string> = {\n checkpoint: \"#6366f1\",\n turn: \"#58a6ff\",\n \"turn-content\": \"#22c55e\",\n memory: \"#f59e0b\",\n};\n\nexport const NODE_TYPE_LABELS: Record<NodeType, string> = {\n checkpoint: \"Checkpoint\",\n turn: \"Turn\",\n \"turn-content\": \"Turn Content\",\n memory: \"Memory\",\n};\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** SVG path for a diamond centered at (0,0) with given half-width/height. */\nfunction diamondPath(hw: number, hh: number): string {\n return `M 0,-${hh} L ${hw},0 L 0,${hh} L -${hw},0 Z`;\n}\n\nfunction nodeOpacity(dedupStatus: string | undefined): number {\n if (!dedupStatus || dedupStatus === \"active\") return 1;\n return 0.5;\n}\n\n// ---------------------------------------------------------------------------\n// Render function\n// ---------------------------------------------------------------------------\n\n/** Render an SVG shape for a node based on its nodeType. */\nexport function renderNodeShape(\n cx: number,\n cy: number,\n radius: number,\n nodeType: NodeType,\n isSelected: boolean,\n isFiltered: boolean,\n dedupStatus: string | undefined,\n): React.ReactElement {\n const fill = isFiltered ? NODE_COLORS[nodeType] ?? \"#6366f1\" : \"#d1d5db\";\n const strokeColor = isSelected ? \"#ef4444\" : \"none\";\n const opacity = nodeOpacity(dedupStatus);\n\n switch (nodeType) {\n case \"checkpoint\":\n return (\n <circle\n cx={cx}\n cy={cy}\n r={isSelected ? radius + 3 : radius}\n fill={fill}\n opacity={opacity}\n stroke={strokeColor}\n strokeWidth={isSelected ? 2 : 0}\n />\n );\n case \"turn\":\n return (\n <circle\n cx={cx}\n cy={cy}\n r={isSelected ? radius + 3 : radius}\n fill=\"none\"\n stroke={fill}\n strokeWidth={2.5}\n opacity={opacity}\n />\n );\n case \"turn-content\":\n return (\n <g>\n <circle\n cx={cx}\n cy={cy}\n r={isSelected ? radius + 3 : radius}\n fill=\"none\"\n stroke={fill}\n strokeWidth={2.5}\n opacity={opacity}\n />\n <circle\n cx={cx}\n cy={cy}\n r={Math.max(3, radius * 0.3)}\n fill={fill}\n opacity={opacity}\n />\n </g>\n );\n case \"memory\":\n const hw = radius * 0.85;\n const hh = radius * 0.85;\n return (\n <path\n d={diamondPath(hw, hh)}\n transform={`translate(${cx}, ${cy})`}\n fill={fill}\n opacity={opacity}\n stroke={strokeColor}\n strokeWidth={isSelected ? 2 : 0}\n />\n );\n }\n}\n","/**\n * memory-map-layout.ts — Force-directed layout engine for MemoryMapTab (D3).\n *\n * Uses Coulomb repulsion + Hooke attraction + center gravity to arrange nodes.\n * No external graph library. Mutates layout in place.\n * Self-contained types: only needs id + edges from graph data.\n */\n\n// ---------------------------------------------------------------------------\n// Types (self-contained — mirrors the subset used by the layout engine)\n// ---------------------------------------------------------------------------\n\ninterface GraphNodeMin {\n readonly id: string;\n readonly nodeType: string;\n readonly label: string;\n readonly summaryTruncated: string;\n readonly tokenEstimate: number;\n readonly timestamp: number;\n readonly dedupStatus: string | undefined;\n readonly raptorLevel: number;\n readonly topicSummary: string | undefined;\n readonly decisionCount: number;\n readonly textSnippet: string;\n readonly sessionId: string;\n}\n\ninterface GraphEdgeMin {\n readonly source: string;\n readonly target: string;\n readonly weight: number;\n readonly type: \"temporal\" | \"semantic\" | \"raptor_parent\";\n}\n\nexport interface Position {\n x: number;\n y: number;\n}\n\nexport interface LayoutNode extends GraphNodeMin {\n pos: Position;\n vx: number;\n vy: number;\n pinned: boolean;\n}\n\nexport interface LayoutEdge {\n source: number;\n target: number;\n weight: number;\n type: GraphEdge[\"type\"];\n}\n\nexport interface Layout {\n nodes: LayoutNode[];\n edges: LayoutEdge[];\n}\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst REPULSION = 5_000;\nconst ATTRACTION = 0.005;\nconst CENTER_GRAVITY = 0.01;\nconst DAMPING = 0.9;\nconst MIN_VELOCITY = 0.1;\nconst SPEED_LIMIT = 50;\n\n// ---------------------------------------------------------------------------\n// Build\n// ---------------------------------------------------------------------------\n\n/** Build initial layout from graph data. */\nexport function buildLayout(data: { nodes: GraphNodeMin[]; edges: GraphEdgeMin[] }): Layout {\n const nodes: LayoutNode[] = data.nodes.map((n, i) => {\n const angle = (2 * Math.PI * i) / Math.max(data.nodes.length, 1);\n const radius = 200;\n return {\n ...n,\n pos: { x: Math.cos(angle) * radius + 300, y: Math.sin(angle) * radius + 200 },\n vx: 0,\n vy: 0,\n pinned: false,\n };\n });\n\n const nodeIndex = new Map<string, number>();\n nodes.forEach((n, i) => nodeIndex.set(n.id, i));\n\n const edges: LayoutEdge[] = data.edges\n .map((e) => {\n const si = nodeIndex.get(e.source);\n const ti = nodeIndex.get(e.target);\n if (si === undefined || ti === undefined || si === ti) return null;\n return { source: si, target: ti, weight: e.weight, type: e.type };\n })\n .filter((e): e is LayoutEdge => e !== null);\n\n return { nodes, edges };\n}\n\n// ---------------------------------------------------------------------------\n// Apply forces\n// ---------------------------------------------------------------------------\n\n/** Apply one iteration of the force simulation. Mutates layout in place. */\nexport function applyForces(layout: Layout): void {\n const { nodes, edges } = layout;\n const n = nodes.length;\n if (n === 0) return;\n\n for (let i = 0; i < n; i++) {\n if (nodes[i].pinned) continue;\n let fx = 0;\n let fy = 0;\n\n // Coulomb repulsion\n for (let j = 0; j < n; j++) {\n if (i === j) continue;\n const dx = nodes[i].pos.x - nodes[j].pos.x;\n const dy = nodes[i].pos.y - nodes[j].pos.y;\n const distSq = dx * dx + dy * dy || 1;\n const force = REPULSION / distSq;\n const dist = Math.sqrt(distSq);\n fx += (dx / dist) * force;\n fy += (dy / dist) * force;\n }\n\n // Hooke attraction along edges\n for (const e of edges) {\n let other: number;\n if (e.source === i) other = e.target;\n else if (e.target === i) other = e.source;\n else continue;\n const dx = nodes[other].pos.x - nodes[i].pos.x;\n const dy = nodes[other].pos.y - nodes[i].pos.y;\n const dist = Math.sqrt(dx * dx + dy * dy) || 1;\n fx += dx * ATTRACTION * e.weight;\n fy += dy * ATTRACTION * e.weight;\n }\n\n // Center gravity\n fx -= nodes[i].pos.x * CENTER_GRAVITY;\n fy -= nodes[i].pos.y * CENTER_GRAVITY;\n\n nodes[i].vx = (nodes[i].vx + fx) * DAMPING;\n nodes[i].vy = (nodes[i].vy + fy) * DAMPING;\n\n const speed = Math.sqrt(nodes[i].vx * nodes[i].vx + nodes[i].vy * nodes[i].vy);\n if (speed > SPEED_LIMIT) {\n nodes[i].vx = (nodes[i].vx / speed) * SPEED_LIMIT;\n nodes[i].vy = (nodes[i].vy / speed) * SPEED_LIMIT;\n }\n\n if (speed > MIN_VELOCITY) {\n nodes[i].pos.x += nodes[i].vx;\n nodes[i].pos.y += nodes[i].vy;\n }\n }\n}\n","/**\n * MemoryMapTab.tsx — D3 memory map with force-directed graph.\n * Node shape encodes nodeType: checkpoint=filled circle, turn=hollow circle,\n * turn-content=hollow+ring, memory=diamond.\n * Fetches from /api/memory-map, renders SVG force layout.\n */\nimport type React from \"react\";\nimport { useEffect, useRef, useState, useCallback, useMemo } from \"react\";\nimport { renderNodeShape, NODE_COLORS, NODE_TYPE_LABELS } from \"../memory-map-shapes.js\";\nimport type { NodeType } from \"../memory-map-shapes.js\";\nimport { buildLayout, applyForces } from \"../memory-map-layout.js\";\nimport type { LayoutNode, LayoutEdge } from \"../memory-map-layout.js\";\n\n// Types (mirror of api-contracts/memory-map.ts — client-side only)\n\ninterface GraphNode {\n id: string;\n sessionId: string;\n label: string;\n summaryTruncated: string;\n tokenEstimate: number;\n timestamp: number;\n dedupStatus: string | undefined;\n raptorLevel: number;\n topicSummary: string | undefined;\n decisionCount: number;\n textSnippet: string;\n /** Source type discriminator for UI node-shape encoding. */\n nodeType: \"checkpoint\" | \"turn\" | \"turn-content\" | \"memory\";\n}\n\ninterface GraphEdge {\n source: string;\n target: string;\n weight: number;\n type: \"temporal\" | \"semantic\" | \"raptor_parent\";\n}\n\ninterface GraphValidationReport {\n readonly gatesRun: number;\n readonly gatesPassed: number;\n readonly dropped: { nodes: number; edges: number };\n readonly warnings: Array<{ gate: string; code: string; count: number }>;\n readonly sources: {\n checkpoint: number;\n turn: number;\n turnContent: number;\n memory: number;\n };\n readonly builtAt: number;\n}\n\ninterface GraphMetadata {\n totalNodes: number;\n totalEdges: number;\n avgWeight: number;\n nodeTypeBreakdown: Record<string, number>;\n edgeTypeBreakdown: Record<string, number>;\n}\n\ninterface MemoryMapResponse {\n nodes: GraphNode[];\n edges: GraphEdge[];\n metadata: GraphMetadata;\n /** Validation report from the 9-gate pipeline. Optional for backward compat. */\n validation?: GraphValidationReport;\n}\n\n// Simulation rendering constants (layout math is in memory-map-layout.ts)\n\nconst MAX_ITERATIONS = 300;\nconst EDGE_ALPHA = 0.3;\nconst SVG_WIDTH = 900;\nconst SVG_HEIGHT = 600;\nconst PADDING = 40;\n\n// Helpers\n\nfunction edgeColor(type: GraphEdge[\"type\"]): string {\n switch (type) {\n case \"temporal\":\n return \"#6366f1\"; // indigo\n case \"semantic\":\n return \"#22c55e\"; // green\n case \"raptor_parent\":\n return \"#f59e0b\"; // amber\n }\n}\n\n// Minimal legend swatch component\ninterface SwatchProps {\n type: \"node\" | \"edge\";\n color: string;\n shape: \"filled-circle\" | \"hollow-circle\" | \"hollow-ring\" | \"diamond\" | \"line\";\n label: string;\n}\nconst Swatch: React.FC<SwatchProps> = ({ color, shape, label }) => {\n const base: React.CSSProperties = {\n display: \"inline-block\",\n width: \"10px\",\n height: \"10px\",\n verticalAlign: \"middle\",\n marginRight: \"3px\",\n borderRadius: shape.startsWith(\"hollow\") || shape === \"filled-circle\" ? \"50%\" : undefined,\n };\n let swatch: JSX.Element;\n if (shape === \"line\") {\n return <span style={{ color, marginRight: \"6px\" }}>{label}</span>;\n } else if (shape === \"filled-circle\") {\n swatch = <span style={{ ...base, backgroundColor: color }} />;\n } else if (shape === \"hollow-circle\") {\n swatch = <span style={{ ...base, border: `2px solid ${color}`, backgroundColor: \"transparent\" }} />;\n } else if (shape === \"hollow-ring\") {\n swatch = (\n <span style={{ ...base, border: `2px solid ${color}`, backgroundColor: \"transparent\", position: \"relative\" }}>\n <span style={{ display: \"block\", width: \"4px\", height: \"4px\", borderRadius: \"50%\", backgroundColor: color, margin: \"2px auto\" }} />\n </span>\n );\n } else {\n swatch = <span style={{ ...base, clipPath: \"polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%)\", backgroundColor: color }} />;\n }\n return <><span className=\"memory-map-legend-item\">{swatch}{label}</span> </>;\n};\n\n// Force simulation engine (delegated to memory-map-layout.ts)\nconst MemoryMapTab: React.FC = () => {\n const [data, setData] = useState<MemoryMapResponse | null>(null);\n const [loading, setLoading] = useState<boolean>(true);\n const [error, setError] = useState<string | null>(null);\n const [selectedNode, setSelectedNode] = useState<GraphNode | null>(null);\n const [searchQuery, setSearchQuery] = useState<string>(\"\");\n const [filterSession] = useState<string>(\"\");\n const [frame, setFrame] = useState<number>(0);\n\n const layoutRef = useRef<{ nodes: LayoutNode[]; edges: LayoutEdge[] } | null>(null);\n const animRef = useRef<number>(0);\n\n // Fetch graph data when filterSession changes\n useEffect(() => {\n let cancelled = false;\n const params = new URLSearchParams();\n if (filterSession) params.set(\"sessionId\", filterSession);\n params.set(\"threshold\", \"0.7\");\n params.set(\"maxEdgesPerNode\", \"4\");\n\n setLoading(true);\n setError(null);\n\n fetch(`/api/memory-map?${params.toString()}`)\n .then((r) => {\n if (!r.ok) throw new Error(`HTTP ${r.status}`);\n return r.json() as Promise<MemoryMapResponse>;\n })\n .then((d) => {\n if (!cancelled) {\n setData(d);\n layoutRef.current = buildLayout(d);\n setFrame(1);\n setLoading(false);\n }\n })\n .catch((e: unknown) => {\n if (!cancelled) {\n setError(e instanceof Error ? e.message : \"Unknown error\");\n setLoading(false);\n }\n });\n\n return () => {\n cancelled = true;\n cancelAnimationFrame(animRef.current);\n };\n }, [filterSession]);\n\n // Run force simulation via requestAnimationFrame\n useEffect(() => {\n if (!layoutRef.current || frame === 0) return;\n\n const layout = layoutRef.current;\n let iter = 0;\n\n const tick = () => {\n if (iter >= MAX_ITERATIONS) return;\n applyForces(layout);\n iter++;\n setFrame(iter);\n animRef.current = requestAnimationFrame(tick);\n };\n\n animRef.current = requestAnimationFrame(tick);\n return () => cancelAnimationFrame(animRef.current);\n }, [frame]);\n\n // Toggle pin / select a node\n const handleNodeClick = useCallback((node: LayoutNode, index: number) => {\n const layout = layoutRef.current;\n if (!layout) return;\n layout.nodes[index].pinned = !layout.nodes[index].pinned;\n if (!layout.nodes[index].pinned) {\n layout.nodes[index].vx = 0;\n layout.nodes[index].vy = 0;\n }\n setSelectedNode((prev) => (prev?.id === node.id ? null : (node as unknown as GraphNode)));\n setFrame((s) => s + 1);\n }, []);\n\n // Filtered node indices for search\n const filteredSet = useMemo<Set<number> | null>(() => {\n const layout = layoutRef.current;\n if (!layout || !searchQuery) return null;\n const q = searchQuery.toLowerCase();\n return new Set(\n layout.nodes\n .map((n, i) =>\n n.summaryTruncated.toLowerCase().includes(q) || n.label.toLowerCase().includes(q) ? i : -1,\n )\n .filter((i) => i >= 0),\n );\n }, [searchQuery]);\n\n // Health indicator helpers (D3)\n\n /**\n * Derive graph health from validation report:\n * green = all gates passed + no warnings\n * yellow = warnings present but no critical gate failed\n * red = a critical gate (gatesRun > gatesPassed) failed\n */\n const graphHealth = useMemo<{ level: \"green\" | \"yellow\" | \"red\"; label: string }>(() => {\n const v = data?.validation;\n if (!v) return { level: \"yellow\", label: \"No validation\" };\n if (v.gatesPassed < v.gatesRun) return { level: \"red\", label: \"Gate failure\" };\n if (v.warnings.length > 0) return { level: \"yellow\", label: `${v.warnings.length} warning(s)` };\n return { level: \"green\", label: \"Healthy\" };\n }, [data?.validation]);\n\n const healthColor: Record<string, string> = {\n green: \"#22c55e\",\n yellow: \"#eab308\",\n red: \"#ef4444\",\n };\n\n // Source availability indicators (D3)\n\n /** Count badge fragments from validation.sources. */\n const countBadge = useMemo<string>(() => {\n const v = data?.validation;\n if (!v) {\n const totalNodes = data?.metadata.totalNodes ?? 0;\n return `${totalNodes} nodes`;\n }\n const parts: string[] = [];\n if (v.sources.checkpoint > 0) parts.push(`${v.sources.checkpoint} checkpoints`);\n if (v.sources.turn > 0) parts.push(`${v.sources.turn} turns`);\n if (v.sources.turnContent > 0) parts.push(`${v.sources.turnContent} turn-content`);\n if (v.sources.memory > 0) parts.push(`${v.sources.memory} memories`);\n if (parts.length === 0) {\n parts.push(`${data?.metadata.totalNodes ?? 0} nodes`);\n }\n return parts.join(\" · \");\n }, [data?.validation, data?.metadata.totalNodes]);\n\n /** Per-source availability: ✓ for available sources, ✗ for empty ones. */\n const sourceAvail = useMemo<string>(() => {\n const v = data?.validation;\n if (!v) return \"\";\n const parts: string[] = [];\n parts.push(v.sources.checkpoint > 0 ? \"✓ checkpoints\" : \"✗ checkpoints\");\n parts.push(v.sources.turn > 0 ? \"✓ turns\" : \"✗ turns\");\n parts.push(v.sources.turnContent > 0 ? \"✓ turn-content\" : \"✗ turn-content\");\n parts.push(v.sources.memory > 0 ? \"✓ memories\" : \"✗ memories\");\n return parts.join(\" | \");\n }, [data?.validation]);\n\n /** Whether all four sources are empty (true empty state). */\n const allSourcesEmpty = useMemo<boolean>(() => {\n const v = data?.validation;\n if (!v) return data?.metadata.totalNodes === 0;\n return (\n v.sources.checkpoint === 0 &&\n v.sources.turn === 0 &&\n v.sources.turnContent === 0 &&\n v.sources.memory === 0\n );\n }, [data?.validation, data?.metadata.totalNodes]);\n\n // Loading state\n\n if (loading) {\n return <div className=\"memory-map-loading\">Loading memory graph...</div>;\n }\n\n // Error state\n\n if (error) {\n return (\n <div className=\"memory-map-error\">\n <p>Failed to load memory map: {error}</p>\n <button\n className=\"memory-map-retry-btn\"\n onClick={() => setFrame((s) => s + 1)}\n >\n Retry\n </button>\n </div>\n );\n }\n\n // Empty state (D2)\n\n if (!data || !layoutRef.current || allSourcesEmpty) {\n return (\n <div className=\"memory-map-empty\">\n <p>Memories appear after your first compaction. The graph shows checkpoints (compaction summaries) linked by semantic similarity and time. Run a longer session or lower the compaction tier to see it sooner.</p>\n </div>\n );\n }\n\n const layout = layoutRef.current;\n\n // Render\n\n return (\n <div className=\"memory-map-container\">\n {/* Toolbar */}\n <div className=\"memory-map-toolbar\">\n <input\n type=\"text\"\n placeholder=\"Search memories...\"\n value={searchQuery}\n onInput={(e: React.FormEvent<HTMLInputElement>) =>\n setSearchQuery((e.target as HTMLInputElement).value)\n }\n className=\"memory-map-search\"\n />\n <span className=\"memory-map-stats\">\n {/* Count badge */}\n <span className=\"memory-map-count-badge\">{countBadge}</span>\n <br />\n <span className=\"memory-map-edges\">{data.edges.length} edges</span>\n </span>\n {/* Graph-health indicator */}\n <span\n className=\"memory-map-health-badge\"\n style={{\n backgroundColor: healthColor[graphHealth.level] ?? \"#6b7280\",\n color: \"#fff\",\n padding: \"2px 8px\",\n borderRadius: \"10px\",\n fontSize: \"11px\",\n fontWeight: 600,\n marginLeft: \"8px\",\n }}\n title={`Validation: ${graphHealth.label}`}\n >\n {graphHealth.level === \"green\" ? \"Healthy\" : graphHealth.level === \"yellow\" ? \"Warnings\" : \"Critical\"}\n </span>\n </div>\n\n {/* Source availability indicators */}\n {sourceAvail ? (\n <div className=\"memory-map-source-avail\" style={{ fontSize: \"11px\", padding: \"2px 12px\", color: \"#6b7280\" }}>\n {sourceAvail}\n </div>\n ) : null}\n\n {/* Legend — node types + edge types */}\n <div className=\"memory-map-legend\">\n <span className=\"memory-map-legend-section\">Nodes:</span>\n <Swatch type=\"node\" color={NODE_COLORS.checkpoint} shape=\"filled-circle\" label=\"Checkpoint\" />\n <Swatch type=\"node\" color={NODE_COLORS.turn} shape=\"hollow-circle\" label=\"Turn\" />\n <Swatch type=\"node\" color={NODE_COLORS[\"turn-content\"]} shape=\"hollow-ring\" label=\"Turn Content\" />\n <Swatch type=\"node\" color={NODE_COLORS.memory} shape=\"diamond\" label=\"Memory\" />\n <span className=\"memory-map-legend-sep\">|</span>\n <span className=\"memory-map-legend-section\">Edges:</span>\n <Swatch type=\"edge\" color={edgeColor(\"semantic\")} shape=\"line\" label=\"Semantic\" />\n <Swatch type=\"edge\" color={edgeColor(\"temporal\")} shape=\"line\" label=\"Temporal\" />\n <Swatch type=\"edge\" color={edgeColor(\"raptor_parent\")} shape=\"line\" label=\"Raptor\" />\n </div>\n\n {/* SVG graph */}\n <svg\n width={SVG_WIDTH}\n height={SVG_HEIGHT}\n viewBox={`0 0 ${SVG_WIDTH} ${SVG_HEIGHT}`}\n className=\"memory-map-svg\"\n >\n {/* Edges */}\n {layout.edges.map((e, i) => {\n const src = layout.nodes[e.source];\n const tgt = layout.nodes[e.target];\n const color = edgeColor(e.type);\n const strokeW = Math.max(1, e.weight * 2.5);\n return (\n <line\n key={`edge-${i}`}\n x1={PADDING + (src.pos.x / (layout.nodes.length || 1)) * (SVG_WIDTH - 2 * PADDING)}\n y1={PADDING + (src.pos.y / (layout.nodes.length || 1)) * (SVG_HEIGHT - 2 * PADDING)}\n x2={PADDING + (tgt.pos.x / (layout.nodes.length || 1)) * (SVG_WIDTH - 2 * PADDING)}\n y2={PADDING + (tgt.pos.y / (layout.nodes.length || 1)) * (SVG_HEIGHT - 2 * PADDING)}\n stroke={color}\n strokeWidth={strokeW}\n opacity={EDGE_ALPHA}\n />\n );\n })}\n\n {/* Nodes — shape encodes nodeType */}\n {layout.nodes.map((n, i) => {\n const cx =\n PADDING + (n.pos.x / (layout.nodes.length || 1)) * (SVG_WIDTH - 2 * PADDING);\n const cy =\n PADDING + (n.pos.y / (layout.nodes.length || 1)) * (SVG_HEIGHT - 2 * PADDING);\n const radius = Math.min(20, 8 + n.tokenEstimate / 500);\n const isFiltered = filteredSet ? filteredSet.has(i) : true;\n const isSelected = selectedNode?.id === n.id;\n const nodeLabel = n.label.length > 20 ? n.label.slice(0, 18) + \"...\" : n.label;\n\n return (\n <g\n key={`node-${i}`}\n className=\"memory-map-node-group\"\n onClick={() => handleNodeClick(n, i)}\n style={{ cursor: \"pointer\" }}\n >\n {renderNodeShape(cx, cy, radius, n.nodeType as NodeType, isSelected, isFiltered, n.dedupStatus)}\n <text\n x={cx}\n y={cy + radius + 12}\n textAnchor=\"middle\"\n fontSize=\"9px\"\n fill=\"#374151\"\n className=\"memory-map-label\"\n >\n {nodeLabel}\n </text>\n </g>\n );\n })}\n </svg>\n\n {/* Node detail panel */}\n {selectedNode ? (\n <div className=\"memory-map-detail\">\n <h3>\n <span\n className=\"memory-map-detail-type\"\n style={{\n display: \"inline-block\",\n fontSize: \"12px\",\n fontWeight: 400,\n color: \"#6b7280\",\n marginRight: \"6px\",\n }}\n >\n {NODE_TYPE_LABELS[selectedNode.nodeType]}\n </span>\n {selectedNode.label}\n </h3>\n <p>{selectedNode.summaryTruncated}</p>\n {selectedNode.topicSummary ? (\n <p className=\"memory-map-topic\">Topic: {selectedNode.topicSummary}</p>\n ) : null}\n <p>\n Tokens: {selectedNode.tokenEstimate} | Decisions: {selectedNode.decisionCount}\n </p>\n <p>Session: {selectedNode.sessionId}</p>\n <p>Raptor Level: {selectedNode.raptorLevel}</p>\n <p className=\"memory-map-snippet\">{selectedNode.textSnippet}</p>\n </div>\n ) : null}\n </div>\n );\n};\n\nexport default MemoryMapTab;\n"],"names":["NODE_COLORS","NODE_TYPE_LABELS","diamondPath","hw","hh","nodeOpacity","dedupStatus","renderNodeShape","cx","cy","radius","nodeType","isSelected","isFiltered","fill","strokeColor","opacity","jsx","REPULSION","ATTRACTION","CENTER_GRAVITY","DAMPING","MIN_VELOCITY","SPEED_LIMIT","buildLayout","data","nodes","n","i","angle","nodeIndex","edges","e","si","ti","applyForces","layout","fx","fy","j","dx","dy","distSq","force","dist","other","speed","MAX_ITERATIONS","EDGE_ALPHA","SVG_WIDTH","SVG_HEIGHT","PADDING","edgeColor","type","Swatch","color","shape","label","base","swatch","jsxs","Fragment","MemoryMapTab","setData","useState","loading","setLoading","error","setError","selectedNode","setSelectedNode","searchQuery","setSearchQuery","filterSession","frame","setFrame","layoutRef","useRef","animRef","useEffect","cancelled","params","r","d","iter","tick","handleNodeClick","useCallback","node","index","prev","s","filteredSet","useMemo","q","graphHealth","v","healthColor","countBadge","parts","sourceAvail","allSourcesEmpty","src","tgt","strokeW","nodeLabel"],"mappings":"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"}
|
package/extensions/dashboard-client/dist/assets/{MetricsTab-CHQscixy.js → MetricsTab-LxNtNzLQ.js}
RENAMED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{j as e,u as o,r as x,i as j,c as N}from"./index-
|
|
2
|
-
//# sourceMappingURL=MetricsTab-
|
|
1
|
+
import{j as e,u as o,r as x,i as j,c as N}from"./index-PRkilPXw.js";function i(s){return s>=1e3?`${(s/1e3).toFixed(1)}s`:`${s.toFixed(0)}ms`}function c({value:s,max:a,colorClass:r}){const p=a>0?Math.max(0,Math.min(100,s/a*100)):0;return e.jsx("div",{className:"perf-bar-track",children:e.jsx("div",{className:`perf-bar-fill ${r}`,style:{width:`${p}%`}})})}function _({perf:s}){const a=Math.max(s.turn_latency_ms.p95,s.provider_latency_ms.p95,1),r=Math.max(s.tps.avg,1);return e.jsxs("div",{className:"perf-chart",children:[e.jsxs("div",{className:"perf-header",children:[e.jsx("h3",{children:"Performance"}),e.jsxs("span",{className:"perf-window",children:[s.windowMinutes,"min window · ",s.sampleCount," samples"]})]}),e.jsxs("div",{className:"perf-grid",children:[e.jsxs("div",{className:"perf-metric",children:[e.jsx("span",{className:"perf-label",children:"Turn latency p50 / p95"}),e.jsxs("span",{className:"perf-value",children:[i(s.turn_latency_ms.p50)," /"," ",i(s.turn_latency_ms.p95)]}),e.jsx(c,{value:s.turn_latency_ms.p95,max:a,colorClass:"perf-blue"})]}),e.jsxs("div",{className:"perf-metric",children:[e.jsx("span",{className:"perf-label",children:"Provider latency p50 / p95"}),e.jsxs("span",{className:"perf-value",children:[i(s.provider_latency_ms.p50)," /"," ",i(s.provider_latency_ms.p95)]}),e.jsx(c,{value:s.provider_latency_ms.p95,max:a,colorClass:"perf-purple"})]}),e.jsxs("div",{className:"perf-metric",children:[e.jsx("span",{className:"perf-label",children:"Tokens/sec (avg)"}),e.jsx("span",{className:"perf-value",children:s.tps.avg.toFixed(1)}),e.jsx(c,{value:s.tps.avg,max:r,colorClass:"perf-green"})]}),e.jsxs("div",{className:"perf-metric",children:[e.jsx("span",{className:"perf-label",children:"Cache hit (avg / latest)"}),e.jsxs("span",{className:"perf-value",children:[s.cache_hit_pct.avg.toFixed(1),"% /"," ",s.cache_hit_pct.latest.toFixed(1),"%"]}),e.jsx(c,{value:s.cache_hit_pct.avg,max:100,colorClass:"perf-green"})]}),e.jsxs("div",{className:"perf-metric",children:[e.jsx("span",{className:"perf-label",children:"DB recompute p95"}),e.jsx("span",{className:"perf-value",children:i(s.db_recompute_ms.p95)}),e.jsx(c,{value:s.db_recompute_ms.p95,max:a,colorClass:"perf-yellow"})]}),e.jsxs("div",{className:"perf-metric",children:[e.jsx("span",{className:"perf-label",children:"Disk write p95"}),e.jsx("span",{className:"perf-value",children:i(s.disk_write_ms.p95)}),e.jsx(c,{value:s.disk_write_ms.p95,max:a,colorClass:"perf-yellow"})]})]}),e.jsxs("div",{className:"perf-resources",children:[e.jsxs("div",{className:"perf-metric",children:[e.jsx("span",{className:"perf-label",children:"RSS"}),e.jsxs("span",{className:"perf-value",children:[s.rss_mb.latest.toFixed(0)," MB"]})]}),e.jsxs("div",{className:"perf-metric",children:[e.jsx("span",{className:"perf-label",children:"Heap"}),e.jsxs("span",{className:"perf-value",children:[s.heap_mb.latest.toFixed(0)," MB"]})]}),e.jsxs("div",{className:"perf-metric",children:[e.jsx("span",{className:"perf-label",children:"CPU user"}),e.jsxs("span",{className:"perf-value",children:[(s.cpu_user_ms.latest/1e3).toFixed(1),"s"]})]}),e.jsxs("div",{className:"perf-metric",children:[e.jsx("span",{className:"perf-label",children:"CPU sys"}),e.jsxs("span",{className:"perf-value",children:[(s.cpu_sys_ms.latest/1e3).toFixed(1),"s"]})]})]}),s.diag&&e.jsxs("div",{className:"perf-diag",children:[e.jsxs("span",{className:"perf-label",children:["Fast-gate fires: ",s.diag.ctxFastGate]}),e.jsxs("span",{className:"perf-label",children:["Live trim fires: ",s.diag.liveTrimFires]}),e.jsxs("span",{className:"perf-label",children:["Live trim replays: ",s.diag.liveTrimReplays]})]})]})}function t(s){return s==null?"—":s>=100?`${Math.round(s)}ms`:`${s.toFixed(1)}ms`}function n(s,a){return s==null||typeof s!="number"?"—":s.toFixed(a)}function u(s){return s==null?"—":String(s)}function l({label:s,value:a}){return e.jsxs("div",{className:"perf-metric",children:[e.jsx("span",{className:"perf-label",children:s}),e.jsx("span",{className:"perf-value",children:a})]})}function d({title:s,children:a}){return e.jsxs("div",{className:"perf-card",children:[e.jsx("h3",{className:"perf-card-title",children:s}),e.jsx("div",{className:"perf-card-body",children:a})]})}function f({perf:s}){const a=`${n(s.cpu_user_ms.latest,1)} / ${n(s.cpu_sys_ms.latest,1)} ms`;return e.jsxs("div",{className:"perf-cards-grid",children:[e.jsxs(d,{title:"Model latency",children:[e.jsx(l,{label:"Turn p50",value:t(s.turn_latency_ms.p50)}),e.jsx(l,{label:"Turn p95",value:t(s.turn_latency_ms.p95)}),e.jsx(l,{label:"Provider p50",value:t(s.provider_latency_ms.p50)}),e.jsx(l,{label:"Provider p95",value:t(s.provider_latency_ms.p95)})]}),e.jsxs(d,{title:"Throughput",children:[e.jsx(l,{label:"TPS (avg)",value:n(s.tps.avg,1)}),e.jsx(l,{label:"Cache hit %",value:`${n(s.cache_hit_pct.avg,1)}%`})]}),e.jsxs(d,{title:"Process",children:[e.jsx(l,{label:"RSS",value:`${n(s.rss_mb.latest,1)} MB`}),e.jsx(l,{label:"Heap",value:`${n(s.heap_mb.latest,1)} MB`}),e.jsx(l,{label:"CPU user / sys",value:a})]}),e.jsxs(d,{title:"Snapshot cost",children:[e.jsx(l,{label:"DB recompute p50",value:t(s.db_recompute_ms.p50)}),e.jsx(l,{label:"DB recompute p95",value:t(s.db_recompute_ms.p95)}),e.jsx(l,{label:"Disk write p50",value:t(s.disk_write_ms.p50)})]}),e.jsxs(d,{title:"TUI lag proxy",children:[e.jsx(l,{label:"Live-trim fires",value:u(s.diag?.liveTrimFires)}),e.jsx(l,{label:"Cache replays",value:u(s.diag?.liveTrimReplays)}),e.jsx(l,{label:"Fast-gate skips",value:u(s.diag?.ctxFastGate)}),e.jsx("span",{className:"perf-card-note",children:"skip vs recompute vs replay cadence"})]})]})}function m({label:s,value:a,title:r}){return e.jsxs("div",{className:"model-field",title:r,children:[e.jsx("span",{className:"model-field-label",children:s}),e.jsx("span",{className:"model-field-value",children:a})]})}function b({name:s,providerName:a,provider:r,inputRate:p,outputRate:h}){const v=a||r||"—";return e.jsxs("div",{className:"model-badge",children:[e.jsx(m,{label:"Model",value:s,title:"The model pi is currently using — its pricing drives the cost figure."}),e.jsx(m,{label:"Provider",value:v,title:"The provider serving the model."}),e.jsx(m,{label:"Input Rate",value:`$${p.toFixed(6)}`,title:"USD per input token, from the model's pricing."}),e.jsx(m,{label:"Output Rate",value:`$${h.toFixed(6)}`,title:"USD per output token, from the model's pricing."})]})}function y(){const{data:s,error:a}=o(x.useCallback(()=>j({minutes:30}),[]),{pollInterval:1e4}),{data:r}=o(x.useCallback(()=>N(),[]),{pollInterval:1e4});return a&&!s?e.jsxs("div",{className:"tab-stub",children:["Error loading perf: ",a.message]}):s?e.jsxs("div",{className:"metrics-tab",children:[r?.model&&e.jsx(b,{name:r.model.name,providerName:r.model.providerName,provider:r.model.provider,inputRate:r.model.inputRate,outputRate:r.model.outputRate}),e.jsx(_,{perf:s}),e.jsx(f,{perf:s}),e.jsxs("div",{className:"perf-footer",children:[s.sampleCount," samples · updated ",s.updatedAt]})]}):e.jsx("div",{className:"tab-stub",children:"Loading perf…"})}export{y as default};
|
|
2
|
+
//# sourceMappingURL=MetricsTab-LxNtNzLQ.js.map
|