pi-mega-compact 0.15.2 → 0.15.3
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/extensions/dashboard-client/dist/assets/{AreaChart-D0hDLKC1.js → AreaChart-CMri0vM1.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{AreaChart-D0hDLKC1.js.map → AreaChart-CMri0vM1.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{BarChart-DSbTWZTQ.js → BarChart-BbkxxSkM.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{BarChart-DSbTWZTQ.js.map → BarChart-BbkxxSkM.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{CacheTab-Bgt2Wuar.js → CacheTab-CiHS6tod.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{CacheTab-Bgt2Wuar.js.map → CacheTab-CiHS6tod.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{EventsTab-CBb7O-6M.js → EventsTab-NnD7EpRm.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{EventsTab-CBb7O-6M.js.map → EventsTab-NnD7EpRm.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{HealthTab-BFMHwnlg.js → HealthTab-Dlt7WTh7.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{HealthTab-BFMHwnlg.js.map → HealthTab-Dlt7WTh7.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{MaintenanceTab-DxIQyeh-.js → MaintenanceTab-DgKBZmdD.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MaintenanceTab-DxIQyeh-.js.map → MaintenanceTab-DgKBZmdD.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{MemoryMapTab-C576-nmM.js → MemoryMapTab-SwTOzuZW.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MemoryMapTab-C576-nmM.js.map → MemoryMapTab-SwTOzuZW.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{MetricsTab-vk8Nqg7k.js → MetricsTab-CWEgBTx0.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MetricsTab-vk8Nqg7k.js.map → MetricsTab-CWEgBTx0.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{OverviewTab-BVaddBS4.js → OverviewTab-B0YDSSMH.js} +4 -4
- package/extensions/dashboard-client/dist/assets/{OverviewTab-BVaddBS4.js.map → OverviewTab-B0YDSSMH.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{ReposTab-CLCd-Gfd.js → ReposTab-CMKZ_a4B.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{ReposTab-CLCd-Gfd.js.map → ReposTab-CMKZ_a4B.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{SessionsTab-CV_qDKZB.js → SessionsTab-D2cCDKB3.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{SessionsTab-CV_qDKZB.js.map → SessionsTab-D2cCDKB3.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{SetupTab-BuIwk7LZ.js → SetupTab-HUAG7xDf.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{SetupTab-BuIwk7LZ.js.map → SetupTab-HUAG7xDf.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{TimeSavedCard-BS5-w4mP.js → TimeSavedCard-NFCsyJhB.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{TimeSavedCard-BS5-w4mP.js.map → TimeSavedCard-NFCsyJhB.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{TurnsTab-DhgjZmEQ.js → TurnsTab-BY7_AZaG.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{TurnsTab-DhgjZmEQ.js.map → TurnsTab-BY7_AZaG.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{WikiTab-CmOAaPnM.js → WikiTab-MbmjqKVQ.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{WikiTab-CmOAaPnM.js.map → WikiTab-MbmjqKVQ.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{button-DyV48WDX.js → button-CoVmU9Vr.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{button-DyV48WDX.js.map → button-CoVmU9Vr.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{card-CfCQ36pG.js → card-Cp9Hpdoo.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{card-CfCQ36pG.js.map → card-Cp9Hpdoo.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{generateCategoricalChart-CGlLggNu.js → generateCategoricalChart-CMM8OUO5.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{generateCategoricalChart-CGlLggNu.js.map → generateCategoricalChart-CMM8OUO5.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{index-tPP7N38D.js → index-CRHmWnRi.js} +3 -3
- package/extensions/dashboard-client/dist/assets/{index-tPP7N38D.js.map → index-CRHmWnRi.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{switch-7461qYip.js → switch-jrYrJJYy.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{switch-7461qYip.js.map → switch-jrYrJJYy.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{toggle-CHyUS1MH.js → toggle-DT1AN5wS.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{toggle-CHyUS1MH.js.map → toggle-DT1AN5wS.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{useSSE-CPIU9hax.js → useSSE-rJH_Rx6K.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{useSSE-CPIU9hax.js.map → useSSE-rJH_Rx6K.js.map} +1 -1
- package/extensions/dashboard-client/dist/index.html +1 -1
- package/extensions/dashboard-client/src/tabs/OverviewTab.tsx +20 -11
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EventsTab-CBb7O-6M.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\";\nimport { Toggle } from \"../components/ui/toggle\";\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\tdefault:\n\t\t\treturn `event=${ev.type ?? \"unknown\"}`;\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=\"flex flex-col gap-3\">\n\t\t\t<div className=\"flex flex-wrap items-center gap-2\" role=\"toolbar\">\n\t\t\t\t{FILTER_TYPES.map((t) => (\n\t\t\t\t\t<Toggle\n\t\t\t\t\t\tkey={t}\n\t\t\t\t\t\tpressed={filter === t}\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</Toggle>\n\t\t\t\t))}\n\t\t\t\t<span className=\"text-xs text-muted-foreground\">{events.length} buffered</span>\n\t\t\t</div>\n\t\t\t<ul className=\"flex flex-col gap-1 font-mono text-xs\">\n\t\t\t\t{filtered.length === 0 && (\n\t\t\t\t\t<li className=\"text-muted-foreground\">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={`rounded-md px-2 py-1.5 transition-colors hover:bg-bg-elevated/40 ${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=\"mt-2 overflow-x-auto rounded-md bg-bg-elevated/60 p-2 text-xs\">\n\t\t\t\t\t\t\t\t\t{JSON.stringify(ev, null, 2)}\n\t\t\t\t\t\t\t\t</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\";\nimport { Toggle } from \"../components/ui/toggle\";\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=\"flex flex-wrap gap-2\" role=\"toolbar\" aria-label=\"Event category filter\">\n\t\t\t{CATEGORIES.map((cat) => (\n\t\t\t\t<Toggle\n\t\t\t\t\tkey={cat}\n\t\t\t\t\tpressed={active === cat}\n\t\t\t\t\tonClick={() => onFilter(cat)}\n\t\t\t\t>\n\t\t\t\t\t{cat}\n\t\t\t\t</Toggle>\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\";\nimport { Badge } from \"../components/ui/badge\";\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 RECALL_TYPES = new Set([\n\t\"recall_inject\",\n\t\"hyde_executed\",\n\t\"recall_metrics\",\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 RECALL_TYPES.has(t);\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 statusVariant =\n\t\tstatus === \"connected\"\n\t\t\t? (\"success\" as const)\n\t\t\t: status === \"error\"\n\t\t\t\t? (\"danger\" as const)\n\t\t\t\t: status === \"connecting\"\n\t\t\t\t\t? (\"warning\" as const)\n\t\t\t\t\t: (\"outline\" as const);\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=\"flex flex-col gap-4\">\n\t\t\t<div className=\"flex items-center justify-between\">\n\t\t\t\t<Badge variant={statusVariant} role=\"status\">\n\t\t\t\t\t<span className=\"mr-1 inline-block h-1.5 w-1.5 rounded-full bg-current\" aria-hidden=\"true\" />\n\t\t\t\t\t{statusLabel}\n\t\t\t\t</Badge>\n\t\t\t\t<span className=\"text-xs text-muted-foreground\">\n\t\t\t\t\t{eventCount} events received\n\t\t\t\t</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","Toggle","idx","cls","CATEGORIES","EventCategoryFilter","active","onFilter","cat","COMPACT_TYPES","CONFIG_TYPES","RECALL_TYPES","CREW_TYPES","GAME_TYPES","matchesCategory","EventsTab","status","eventCount","useSSE","category","setCategory","statusVariant","statusLabel","Badge"],"mappings":"wIAiBA,MAAMA,EAAsC,CAC3C,cAAe,aACf,YAAa,aACb,gBAAiB,aACjB,aAAc,aACd,aAAc,UACd,cAAe,UACf,gBAAiB,UACjB,qBAAsB,gBACtB,cAAe,YACf,gBAAiB,YACjB,eAAgB,YAChB,cAAe,YACf,sBAAuB,UACvB,kBAAmB,UACnB,mBAAoB,UACpB,eAAgB,YAChB,kBAAmB,UACnB,kBAAmB,UACnB,gBAAiB,UACjB,kBAAmB,UACnB,YAAa,SACd,EAGMC,EAAe,CACpB,MACA,gBACA,cACA,gBACA,sBACD,EAKA,SAASC,EAAUC,EAAsB,CACxC,OAAQA,EAAG,KAAA,CACV,IAAK,gBACJ,MAAO,WAAWA,EAAG,OAAO,YAAYA,EAAG,UAAU,MAAM,EAAG,CAAC,CAAC,GACjE,IAAK,cACJ,MAAO,SAASA,EAAG,WAAW,OAAOA,EAAG,OAAO,OAAOA,EAAG,aAAa,MAAM,EAAG,CAAC,CAAC,GAClF,IAAK,kBACJ,MAAO,YAAYA,EAAG,QAAQ,eAAeA,EAAG,SAAS,WAAWA,EAAG,KAAK,GAC7E,IAAK,eACJ,MAAO,UAAUA,EAAG,MAAM,GAC3B,IAAK,eACJ,MAAO,GAAGA,EAAG,IAAI,MAAMA,EAAG,EAAE,QAAQA,EAAG,UAAU,IAClD,IAAK,gBACJ,MAAO,GAAGA,EAAG,YAAY,IAAIA,EAAG,KAAK,GACtC,IAAK,kBACJ,MAAO,GAAGA,EAAG,SAAS,OAAOA,EAAG,QAAQ,IACzC,IAAK,uBACJ,MAAO,MAAMA,EAAG,aAAa,MAAM,EAAG,CAAC,CAAC,eAAeA,EAAG,aAAa,GACxE,IAAK,gBACJ,MAAO,MAAMA,EAAG,MAAM,MAAM,EAAG,EAAE,CAAC,YAAYA,EAAG,MAAM,QAAQA,EAAG,MAAM,GACzE,IAAK,kBACJ,MAAO,SAASA,EAAG,KAAK,WAAWA,EAAG,MAAM,GAC7C,IAAK,iBACJ,MAAO,OAAOA,EAAG,GAAG,GACrB,IAAK,gBACJ,MAAO,UAAUA,EAAG,MAAM,GAC3B,IAAK,wBACJ,MAAO,UAAUA,EAAG,YAAY,SAASA,EAAG,WAAW,GACxD,IAAK,oBACJ,MAAO,QAAQA,EAAG,SAAS,UAAUA,EAAG,SAAS,GAClD,IAAK,qBACJ,MAAO,SAASA,EAAG,WAAW,UAAUA,EAAG,KAAK,WAAWA,EAAG,MAAM,GACrE,IAAK,iBACJ,MAAO,QAAQA,EAAG,UAAU,MAAM,EAAG,CAAC,CAAC,QAAQA,EAAG,OAAO,gBAAgB,QAAQ,KAAK,MAAMA,EAAG,OAAO,CAAC,IACxG,IAAK,oBACL,IAAK,oBACL,IAAK,kBACL,IAAK,oBACL,IAAK,cACJ,MAAO,SAASA,EAAG,IAAI,GACxB,QACC,MAAO,SAASA,EAAG,MAAQ,SAAS,EAAA,CAEvC,CAEA,SAASC,EAASC,EAAoB,CACrC,GAAI,CAEH,OADU,IAAI,KAAKA,CAAE,EACZ,mBAAA,CACV,MAAQ,CACP,OAAOA,CACR,CACD,CAEO,SAASC,EAAY,CAAE,OAAAC,GAAgD,CAC7E,KAAM,CAACC,EAAQC,CAAS,EAAIC,EAAAA,SAAqB,KAAK,EAChD,CAACC,EAAUC,CAAW,EAAIF,EAAAA,SAAwB,IAAI,EAEtDG,EAAWC,EAAAA,QAAQ,KAEvBN,IAAW,MAAQD,EAASA,EAAO,OAAQQ,GAAMA,EAAE,OAASP,CAAM,GAEvD,MAAM,IAAc,EAAE,QAAA,EAChC,CAACD,EAAQC,CAAM,CAAC,EAEnB,OACCQ,EAAAA,KAAC,MAAA,CAAI,UAAU,sBACd,SAAA,CAAAA,EAAAA,KAAC,MAAA,CAAI,UAAU,oCAAoC,KAAK,UACtD,SAAA,CAAAf,EAAa,IAAKgB,GAClBC,EAAAA,IAACC,EAAA,CAEA,QAASX,IAAWS,EACpB,QAAS,IAAMR,EAAUQ,CAAC,EAEzB,aAAM,MAAQ,MAAQA,EAAE,QAAQ,KAAM,GAAG,CAAA,EAJrCA,CAAA,CAMN,EACDD,EAAAA,KAAC,OAAA,CAAK,UAAU,gCAAiC,SAAA,CAAAT,EAAO,OAAO,WAAA,CAAA,CAAS,CAAA,EACzE,EACAS,EAAAA,KAAC,KAAA,CAAG,UAAU,wCACZ,SAAA,CAAAH,EAAS,SAAW,GACpBK,MAAC,KAAA,CAAG,UAAU,wBAAwB,SAAA,iBAAc,EAEpDL,EAAS,IAAI,CAACV,EAAIiB,IAAQ,CAC1B,MAAMC,EAAMrB,EAAYG,EAAG,IAAI,GAAK,aACpC,OACCa,EAAAA,KAAC,KAAA,CAEA,UAAW,oEAAoEK,CAAG,GAClF,QAAS,IAAMT,EAAYD,IAAaS,EAAM,KAAOA,CAAG,EAExD,SAAA,CAAAF,MAAC,QAAK,UAAU,UAAW,SAAAd,EAASD,EAAG,EAAE,EAAE,EAAQ,IACnDe,EAAAA,IAAC,OAAA,CAAK,UAAU,UAAW,WAAG,KAAK,EAAQ,UAC1C,OAAA,CAAK,UAAU,aAAc,SAAAhB,EAAUC,CAAE,EAAE,EAC3CQ,IAAaS,GACbF,EAAAA,IAAC,MAAA,CAAI,UAAU,gEACb,SAAA,KAAK,UAAUf,EAAI,KAAM,CAAC,CAAA,CAC5B,CAAA,CAAA,EAVI,GAAGA,EAAG,EAAE,IAAIiB,CAAG,EAAA,CAcvB,CAAC,CAAA,CAAA,CACF,CAAA,EACD,CAEF,CC5IA,MAAME,EAAuC,CAC5C,MACA,UACA,SACA,SACA,OACA,MACD,EASO,SAASC,EAAoB,CACnC,OAAAC,EACA,SAAAC,CACD,EAAiD,CAChD,OACCP,EAAAA,IAAC,MAAA,CAAI,UAAU,uBAAuB,KAAK,UAAU,aAAW,wBAC9D,SAAAI,EAAW,IAAKI,GAChBR,EAAAA,IAACC,EAAA,CAEA,QAASK,IAAWE,EACpB,QAAS,IAAMD,EAASC,CAAG,EAE1B,SAAAA,CAAA,EAJIA,CAAA,CAMN,EACF,CAEF,CChCA,MAAMC,MAAoB,IAAI,CAC7B,gBACA,cACA,kBACA,cACD,CAAC,EACKC,MAAmB,IAAI,CAC5B,iBACA,gBACA,eACA,gBACA,kBACA,kBACA,sBACD,CAAC,EACKC,MAAmB,IAAI,CAC5B,gBACA,gBACA,gBACD,CAAC,EACKC,MAAiB,IAAI,CAC1B,wBACA,oBACA,oBACD,CAAC,EACKC,MAAiB,IAAI,CAC1B,oBACA,oBACA,kBACA,oBACA,aACD,CAAC,EAED,SAASC,EAAgB7B,EAAcuB,EAA6B,CACnE,GAAIA,IAAQ,MAAO,MAAO,GAC1B,MAAMT,EAAId,EAAG,KACb,OAAIuB,IAAQ,UAAkBC,EAAc,IAAIV,CAAC,EAC7CS,IAAQ,SAAiBG,EAAa,IAAIZ,CAAC,EAC3CS,IAAQ,SAAiBE,EAAa,IAAIX,CAAC,EAC3CS,IAAQ,OAAeI,EAAW,IAAIb,CAAC,EACpCc,EAAW,IAAId,CAAC,CACxB,CAEA,SAAwBgB,GAAgC,CACvD,KAAM,CAAE,OAAA1B,EAAQ,OAAA2B,EAAQ,WAAAC,CAAA,EAAeC,EAAA,EACjC,CAACC,EAAUC,CAAW,EAAI5B,EAAAA,SAAwB,KAAK,EAEvDG,EAAWC,EAAAA,QAChB,IAAMP,EAAO,OAAQQ,GAAMiB,EAAgBjB,EAAGsB,CAAQ,CAAC,EACvD,CAAC9B,EAAQ8B,CAAQ,CAAA,EAGZE,EACLL,IAAW,YACP,UACDA,IAAW,QACT,SACDA,IAAW,aACT,UACA,UACDM,EACLN,IAAW,YACR,YACAA,IAAW,aACV,cACAA,IAAW,QACV,QACA,eAEN,OACClB,EAAAA,KAAC,MAAA,CAAI,UAAU,sBACd,SAAA,CAAAA,EAAAA,KAAC,MAAA,CAAI,UAAU,oCACd,SAAA,CAAAA,EAAAA,KAACyB,EAAA,CAAM,QAASF,EAAe,KAAK,SACnC,SAAA,CAAArB,EAAAA,IAAC,OAAA,CAAK,UAAU,wDAAwD,cAAY,OAAO,EAC1FsB,CAAA,EACF,EACAxB,EAAAA,KAAC,OAAA,CAAK,UAAU,gCACd,SAAA,CAAAmB,EAAW,kBAAA,CAAA,CACb,CAAA,EACD,EACAjB,EAAAA,IAACK,EAAA,CAAoB,OAAQc,EAAU,SAAUC,EAAa,EAC9DpB,EAAAA,IAACZ,EAAA,CAAY,OAAQO,CAAA,CAAU,CAAA,EAChC,CAEF"}
|
|
1
|
+
{"version":3,"file":"EventsTab-NnD7EpRm.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\";\nimport { Toggle } from \"../components/ui/toggle\";\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\tdefault:\n\t\t\treturn `event=${ev.type ?? \"unknown\"}`;\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=\"flex flex-col gap-3\">\n\t\t\t<div className=\"flex flex-wrap items-center gap-2\" role=\"toolbar\">\n\t\t\t\t{FILTER_TYPES.map((t) => (\n\t\t\t\t\t<Toggle\n\t\t\t\t\t\tkey={t}\n\t\t\t\t\t\tpressed={filter === t}\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</Toggle>\n\t\t\t\t))}\n\t\t\t\t<span className=\"text-xs text-muted-foreground\">{events.length} buffered</span>\n\t\t\t</div>\n\t\t\t<ul className=\"flex flex-col gap-1 font-mono text-xs\">\n\t\t\t\t{filtered.length === 0 && (\n\t\t\t\t\t<li className=\"text-muted-foreground\">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={`rounded-md px-2 py-1.5 transition-colors hover:bg-bg-elevated/40 ${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=\"mt-2 overflow-x-auto rounded-md bg-bg-elevated/60 p-2 text-xs\">\n\t\t\t\t\t\t\t\t\t{JSON.stringify(ev, null, 2)}\n\t\t\t\t\t\t\t\t</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\";\nimport { Toggle } from \"../components/ui/toggle\";\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=\"flex flex-wrap gap-2\" role=\"toolbar\" aria-label=\"Event category filter\">\n\t\t\t{CATEGORIES.map((cat) => (\n\t\t\t\t<Toggle\n\t\t\t\t\tkey={cat}\n\t\t\t\t\tpressed={active === cat}\n\t\t\t\t\tonClick={() => onFilter(cat)}\n\t\t\t\t>\n\t\t\t\t\t{cat}\n\t\t\t\t</Toggle>\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\";\nimport { Badge } from \"../components/ui/badge\";\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 RECALL_TYPES = new Set([\n\t\"recall_inject\",\n\t\"hyde_executed\",\n\t\"recall_metrics\",\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 RECALL_TYPES.has(t);\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 statusVariant =\n\t\tstatus === \"connected\"\n\t\t\t? (\"success\" as const)\n\t\t\t: status === \"error\"\n\t\t\t\t? (\"danger\" as const)\n\t\t\t\t: status === \"connecting\"\n\t\t\t\t\t? (\"warning\" as const)\n\t\t\t\t\t: (\"outline\" as const);\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=\"flex flex-col gap-4\">\n\t\t\t<div className=\"flex items-center justify-between\">\n\t\t\t\t<Badge variant={statusVariant} role=\"status\">\n\t\t\t\t\t<span className=\"mr-1 inline-block h-1.5 w-1.5 rounded-full bg-current\" aria-hidden=\"true\" />\n\t\t\t\t\t{statusLabel}\n\t\t\t\t</Badge>\n\t\t\t\t<span className=\"text-xs text-muted-foreground\">\n\t\t\t\t\t{eventCount} events received\n\t\t\t\t</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","Toggle","idx","cls","CATEGORIES","EventCategoryFilter","active","onFilter","cat","COMPACT_TYPES","CONFIG_TYPES","RECALL_TYPES","CREW_TYPES","GAME_TYPES","matchesCategory","EventsTab","status","eventCount","useSSE","category","setCategory","statusVariant","statusLabel","Badge"],"mappings":"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"}
|
package/extensions/dashboard-client/dist/assets/{HealthTab-BFMHwnlg.js → HealthTab-Dlt7WTh7.js}
RENAMED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{r as d,j as e,B as g}from"./index-
|
|
2
|
-
//# sourceMappingURL=HealthTab-
|
|
1
|
+
import{r as d,j as e,B as g}from"./index-CRHmWnRi.js";import{fetchContextHealth as N}from"./health-lSeDFTz7.js";import{C as i,c as h,a as u,b as j}from"./card-Cp9Hpdoo.js";function m(s){return s>.8?"#4ade80":s>=.5?"#facc15":"#f87171"}function v({score:s}){const l=2*Math.PI*70,o=l*(1-Math.max(0,Math.min(1,s))),n=m(s);return e.jsxs("div",{className:"health-gauge-wrap",children:[e.jsxs("svg",{width:"180",height:"180",viewBox:"0 0 180 180",children:[e.jsx("circle",{cx:"90",cy:"90",r:70,fill:"none",stroke:"#1e293b",strokeWidth:"14"}),e.jsx("circle",{cx:"90",cy:"90",r:70,fill:"none",stroke:n,strokeWidth:"14",strokeDasharray:l,strokeDashoffset:o,strokeLinecap:"round",transform:"rotate(-90 90 90)",style:{transition:"stroke-dashoffset 0.5s ease"}}),e.jsx("text",{x:"90",y:"85",textAnchor:"middle",fontSize:"32",fontWeight:"bold",fill:n,children:(s*100).toFixed(0)}),e.jsx("text",{x:"90",y:"108",textAnchor:"middle",fontSize:"14",fill:"#94a3b8",children:"/ 100"})]}),e.jsx("div",{className:"health-gauge-label",children:"Context Health"})]})}function y({trend:s}){if(s.length<2)return e.jsx("div",{className:"health-spark-empty",children:"No trend data yet"});const r=400,l=60,o=1,n=0,f=s.map((a,p)=>{const t=p/(s.length-1)*r,b=l-(a-n)/(o-n)*l;return`${t.toFixed(1)},${b.toFixed(1)}`}),c=s[s.length-1];return e.jsx("svg",{width:r,height:l,viewBox:`0 0 ${r} ${l}`,className:"health-spark",children:e.jsx("polyline",{points:f.join(" "),fill:"none",stroke:m(c),strokeWidth:"2"})})}function x({label:s,value:r}){return e.jsxs("div",{className:"health-subscore",children:[e.jsx("span",{className:"health-subscore-label",children:s}),e.jsx("div",{className:"health-subscore-bar",children:e.jsx("div",{className:"health-subscore-fill",style:{width:`${r*100}%`,background:m(r)}})}),e.jsxs("span",{className:"health-subscore-val",children:[(r*100).toFixed(0),"%"]})]})}function H(){const[s,r]=d.useState(null),[l,o]=d.useState(null),[n,f]=d.useState(!0),c=d.useCallback(()=>{N().then(r).catch(t=>o(t instanceof Error?t.message:String(t))).finally(()=>f(!1))},[]);if(d.useEffect(()=>{c();const t=setInterval(c,5e3);return()=>clearInterval(t)},[c]),n&&!s)return e.jsx("div",{className:"health-loading",children:"Loading health data…"});if(l&&!s)return e.jsxs("div",{className:"health-error",children:["Error: ",l]});if(!s)return e.jsx("div",{className:"health-empty",children:"No health data available."});const a=s.latest,p=a?.composite??0;return e.jsxs("div",{className:"flex flex-col gap-4",children:[e.jsxs("div",{className:"grid grid-cols-1 gap-4 md:grid-cols-2",children:[e.jsx(i,{children:e.jsx(h,{className:"flex justify-center",children:e.jsx(v,{score:p})})}),e.jsxs(i,{children:[e.jsx(u,{children:e.jsx(j,{children:"Sub-Scores"})}),e.jsxs(h,{children:[e.jsx(x,{label:"Drift",value:a?.driftScore??0}),e.jsx(x,{label:"Output Quality",value:a?.outputQuality??0}),e.jsx(x,{label:"Error Rate",value:a?.errorScore??0}),e.jsx(x,{label:"Cache Health",value:a?.cacheHealth??0}),e.jsx(x,{label:"Cache Poison",value:a?.cachePoison??0})]})]})]}),e.jsxs(i,{children:[e.jsx(u,{children:e.jsxs(j,{children:["Health Trend (last ",s.trend.length," turns)"]})}),e.jsx(h,{children:e.jsx(y,{trend:s.trend})})]}),s.alerts.length>0&&e.jsxs(i,{children:[e.jsx(u,{children:e.jsxs(j,{children:["Recent Alerts (",s.alerts.length,")"]})}),e.jsx(h,{children:e.jsx("div",{className:"flex flex-col gap-1",children:s.alerts.slice(-10).map(t=>e.jsxs("div",{className:"flex items-center gap-3 border-b border-border/50 py-1 text-sm",children:[e.jsx("span",{className:"text-xs text-muted-foreground",children:new Date(t.ts).toLocaleTimeString()}),e.jsx("span",{className:"text-muted-foreground",children:t.modelId??"(unknown)"}),e.jsx("span",{style:{color:m(t.composite)},children:(t.composite*100).toFixed(0)}),t.cachePoison<.3&&e.jsx(g,{variant:"danger",children:"CACHE POISON"})]},`${t.ts}-${t.turnIndex}`))})})]}),s.perModel.length>0&&e.jsxs(i,{children:[e.jsx(u,{children:e.jsx(j,{children:"Health by Model"})}),e.jsx(h,{children:e.jsxs("table",{className:"w-full border-collapse text-sm",children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{className:"border-b border-border px-3 py-2 text-left font-medium text-muted-foreground",children:"Model"}),e.jsx("th",{className:"border-b border-border px-3 py-2 text-left font-medium text-muted-foreground",children:"Avg Health"}),e.jsx("th",{className:"border-b border-border px-3 py-2 text-left font-medium text-muted-foreground",children:"Samples"})]})}),e.jsx("tbody",{children:s.perModel.map(t=>e.jsxs("tr",{className:"border-b border-border/50",children:[e.jsx("td",{className:"px-3 py-2",children:t.modelId}),e.jsxs("td",{className:"px-3 py-2",style:{color:m(t.avgComposite)},children:[(t.avgComposite*100).toFixed(0),"%"]}),e.jsx("td",{className:"px-3 py-2",children:t.sampleCount})]},t.modelId))})]})})]})]})}export{H as default};
|
|
2
|
+
//# sourceMappingURL=HealthTab-Dlt7WTh7.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HealthTab-BFMHwnlg.js","sources":["../../src/tabs/HealthTab.tsx"],"sourcesContent":["/**\n * HealthTab.tsx — Context Health dashboard tab.\n * Shows composite health gauge, sparkline trend, sub-score bars,\n * alerts, and per-model comparison. Polls /api/context-health every 5s.\n */\n\nimport { useState, useEffect, useCallback } from \"react\";\nimport { fetchContextHealth, type ContextHealthResponse } from \"../api/health\";\nimport { Card, CardContent, CardHeader, CardTitle } from \"../components/ui/card\";\nimport { Badge } from \"../components/ui/badge\";\n\nfunction healthColor(score: number): string {\n\tif (score > 0.8) return \"#4ade80\";\n\tif (score >= 0.5) return \"#facc15\";\n\treturn \"#f87171\";\n}\n\nfunction HealthGauge({ score }: { score: number }): React.ReactElement {\n\tconst r = 70;\n\tconst circ = 2 * Math.PI * r;\n\tconst offset = circ * (1 - Math.max(0, Math.min(1, score)));\n\tconst color = healthColor(score);\n\treturn (\n\t\t<div className=\"health-gauge-wrap\">\n\t\t\t<svg width=\"180\" height=\"180\" viewBox=\"0 0 180 180\">\n\t\t\t\t<circle cx=\"90\" cy=\"90\" r={r} fill=\"none\" stroke=\"#1e293b\" strokeWidth=\"14\" />\n\t\t\t\t<circle\n\t\t\t\t\tcx=\"90\"\n\t\t\t\t\tcy=\"90\"\n\t\t\t\t\tr={r}\n\t\t\t\t\tfill=\"none\"\n\t\t\t\t\tstroke={color}\n\t\t\t\t\tstrokeWidth=\"14\"\n\t\t\t\t\tstrokeDasharray={circ}\n\t\t\t\t\tstrokeDashoffset={offset}\n\t\t\t\t\tstrokeLinecap=\"round\"\n\t\t\t\t\ttransform=\"rotate(-90 90 90)\"\n\t\t\t\t\tstyle={{ transition: \"stroke-dashoffset 0.5s ease\" }}\n\t\t\t\t/>\n\t\t\t\t<text x=\"90\" y=\"85\" textAnchor=\"middle\" fontSize=\"32\" fontWeight=\"bold\" fill={color}>\n\t\t\t\t\t{(score * 100).toFixed(0)}\n\t\t\t\t</text>\n\t\t\t\t<text x=\"90\" y=\"108\" textAnchor=\"middle\" fontSize=\"14\" fill=\"#94a3b8\">/ 100</text>\n\t\t\t</svg>\n\t\t\t<div className=\"health-gauge-label\">Context Health</div>\n\t\t</div>\n\t);\n}\n\nfunction HealthSparkline({ trend }: { trend: number[] }): React.ReactElement {\n\tif (trend.length < 2) return <div className=\"health-spark-empty\">No trend data yet</div>;\n\tconst w = 400;\n\tconst h = 60;\n\tconst max = 1;\n\tconst min = 0;\n\tconst pts = trend.map((v, i) => {\n\t\tconst x = (i / (trend.length - 1)) * w;\n\t\tconst y = h - ((v - min) / (max - min)) * h;\n\t\treturn `${x.toFixed(1)},${y.toFixed(1)}`;\n\t});\n\tconst last = trend[trend.length - 1];\n\treturn (\n\t\t<svg width={w} height={h} viewBox={`0 0 ${w} ${h}`} className=\"health-spark\">\n\t\t\t<polyline points={pts.join(\" \")} fill=\"none\" stroke={healthColor(last)} strokeWidth=\"2\" />\n\t\t</svg>\n\t);\n}\n\nfunction SubScoreBar({ label, value }: { label: string; value: number }): React.ReactElement {\n\treturn (\n\t\t<div className=\"health-subscore\">\n\t\t\t<span className=\"health-subscore-label\">{label}</span>\n\t\t\t<div className=\"health-subscore-bar\">\n\t\t\t\t<div\n\t\t\t\t\tclassName=\"health-subscore-fill\"\n\t\t\t\t\tstyle={{ width: `${value * 100}%`, background: healthColor(value) }}\n\t\t\t\t/>\n\t\t\t</div>\n\t\t\t<span className=\"health-subscore-val\">{(value * 100).toFixed(0)}%</span>\n\t\t</div>\n\t);\n}\n\nexport default function HealthTab(): React.ReactElement {\n\tconst [data, setData] = useState<ContextHealthResponse | null>(null);\n\tconst [err, setError] = useState<string | null>(null);\n\tconst [loading, setLoading] = useState(true);\n\n\tconst poll = useCallback(() => {\n\t\tfetchContextHealth()\n\t\t\t.then(setData)\n\t\t\t.catch((e: unknown) => setError(e instanceof Error ? e.message : String(e)))\n\t\t\t.finally(() => setLoading(false));\n\t}, []);\n\n\tuseEffect(() => {\n\t\tpoll();\n\t\tconst id = setInterval(poll, 5000);\n\t\treturn () => clearInterval(id);\n\t}, [poll]);\n\n\tif (loading && !data) return <div className=\"health-loading\">Loading health data…</div>;\n\tif (err && !data) return <div className=\"health-error\">Error: {err}</div>;\n\tif (!data) return <div className=\"health-empty\">No health data available.</div>;\n\n\tconst latest = data.latest;\n\tconst composite = latest?.composite ?? 0;\n\n\treturn (\n\t\t<div className=\"flex flex-col gap-4\">\n\t\t\t<div className=\"grid grid-cols-1 gap-4 md:grid-cols-2\">\n\t\t\t\t<Card>\n\t\t\t\t\t<CardContent className=\"flex justify-center\">\n\t\t\t\t\t\t<HealthGauge score={composite} />\n\t\t\t\t\t</CardContent>\n\t\t\t\t</Card>\n\t\t\t\t<Card>\n\t\t\t\t\t<CardHeader>\n\t\t\t\t\t\t<CardTitle>Sub-Scores</CardTitle>\n\t\t\t\t\t</CardHeader>\n\t\t\t\t\t<CardContent>\n\t\t\t\t\t\t<SubScoreBar label=\"Drift\" value={latest?.driftScore ?? 0} />\n\t\t\t\t\t\t<SubScoreBar label=\"Output Quality\" value={latest?.outputQuality ?? 0} />\n\t\t\t\t\t\t<SubScoreBar label=\"Error Rate\" value={latest?.errorScore ?? 0} />\n\t\t\t\t\t\t<SubScoreBar label=\"Cache Health\" value={latest?.cacheHealth ?? 0} />\n\t\t\t\t\t\t<SubScoreBar label=\"Cache Poison\" value={latest?.cachePoison ?? 0} />\n\t\t\t\t\t</CardContent>\n\t\t\t\t</Card>\n\t\t\t</div>\n\n\t\t\t<Card>\n\t\t\t\t<CardHeader>\n\t\t\t\t\t<CardTitle>Health Trend (last {data.trend.length} turns)</CardTitle>\n\t\t\t\t</CardHeader>\n\t\t\t\t<CardContent>\n\t\t\t\t\t<HealthSparkline trend={data.trend} />\n\t\t\t\t</CardContent>\n\t\t\t</Card>\n\n\t\t\t{data.alerts.length > 0 && (\n\t\t\t\t<Card>\n\t\t\t\t\t<CardHeader>\n\t\t\t\t\t\t<CardTitle>Recent Alerts ({data.alerts.length})</CardTitle>\n\t\t\t\t\t</CardHeader>\n\t\t\t\t\t<CardContent>\n\t\t\t\t\t\t<div className=\"flex flex-col gap-1\">\n\t\t\t\t\t\t\t{data.alerts.slice(-10).map((a) => (\n\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\tkey={`${a.ts}-${a.turnIndex}`}\n\t\t\t\t\t\t\t\t\tclassName=\"flex items-center gap-3 border-b border-border/50 py-1 text-sm\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<span className=\"text-xs text-muted-foreground\">{new Date(a.ts).toLocaleTimeString()}</span>\n\t\t\t\t\t\t\t\t\t<span className=\"text-muted-foreground\">{a.modelId ?? \"(unknown)\"}</span>\n\t\t\t\t\t\t\t\t\t<span style={{ color: healthColor(a.composite) }}>\n\t\t\t\t\t\t\t\t\t\t{(a.composite * 100).toFixed(0)}\n\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t{a.cachePoison < 0.3 && <Badge variant=\"danger\">CACHE POISON</Badge>}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</CardContent>\n\t\t\t\t</Card>\n\t\t\t)}\n\n\t\t\t{data.perModel.length > 0 && (\n\t\t\t\t<Card>\n\t\t\t\t\t<CardHeader>\n\t\t\t\t\t\t<CardTitle>Health by Model</CardTitle>\n\t\t\t\t\t</CardHeader>\n\t\t\t\t\t<CardContent>\n\t\t\t\t\t\t<table className=\"w-full border-collapse text-sm\">\n\t\t\t\t\t\t\t<thead>\n\t\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t\t\t<th className=\"border-b border-border px-3 py-2 text-left font-medium text-muted-foreground\">Model</th>\n\t\t\t\t\t\t\t\t\t<th className=\"border-b border-border px-3 py-2 text-left font-medium text-muted-foreground\">Avg Health</th>\n\t\t\t\t\t\t\t\t\t<th className=\"border-b border-border px-3 py-2 text-left font-medium text-muted-foreground\">Samples</th>\n\t\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t</thead>\n\t\t\t\t\t\t\t<tbody>\n\t\t\t\t\t\t\t\t{data.perModel.map((m) => (\n\t\t\t\t\t\t\t\t\t<tr key={m.modelId} className=\"border-b border-border/50\">\n\t\t\t\t\t\t\t\t\t\t<td className=\"px-3 py-2\">{m.modelId}</td>\n\t\t\t\t\t\t\t\t\t\t<td className=\"px-3 py-2\" style={{ color: healthColor(m.avgComposite) }}>\n\t\t\t\t\t\t\t\t\t\t\t{(m.avgComposite * 100).toFixed(0)}%\n\t\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t\t<td className=\"px-3 py-2\">{m.sampleCount}</td>\n\t\t\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t</tbody>\n\t\t\t\t\t\t</table>\n\t\t\t\t\t</CardContent>\n\t\t\t\t</Card>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n"],"names":["healthColor","score","HealthGauge","circ","offset","color","jsxs","jsx","HealthSparkline","trend","w","h","max","min","pts","v","i","x","y","last","SubScoreBar","label","value","HealthTab","data","setData","useState","err","setError","loading","setLoading","poll","useCallback","fetchContextHealth","e","useEffect","id","latest","composite","Card","CardContent","CardHeader","CardTitle","a","Badge","m"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"HealthTab-Dlt7WTh7.js","sources":["../../src/tabs/HealthTab.tsx"],"sourcesContent":["/**\n * HealthTab.tsx — Context Health dashboard tab.\n * Shows composite health gauge, sparkline trend, sub-score bars,\n * alerts, and per-model comparison. Polls /api/context-health every 5s.\n */\n\nimport { useState, useEffect, useCallback } from \"react\";\nimport { fetchContextHealth, type ContextHealthResponse } from \"../api/health\";\nimport { Card, CardContent, CardHeader, CardTitle } from \"../components/ui/card\";\nimport { Badge } from \"../components/ui/badge\";\n\nfunction healthColor(score: number): string {\n\tif (score > 0.8) return \"#4ade80\";\n\tif (score >= 0.5) return \"#facc15\";\n\treturn \"#f87171\";\n}\n\nfunction HealthGauge({ score }: { score: number }): React.ReactElement {\n\tconst r = 70;\n\tconst circ = 2 * Math.PI * r;\n\tconst offset = circ * (1 - Math.max(0, Math.min(1, score)));\n\tconst color = healthColor(score);\n\treturn (\n\t\t<div className=\"health-gauge-wrap\">\n\t\t\t<svg width=\"180\" height=\"180\" viewBox=\"0 0 180 180\">\n\t\t\t\t<circle cx=\"90\" cy=\"90\" r={r} fill=\"none\" stroke=\"#1e293b\" strokeWidth=\"14\" />\n\t\t\t\t<circle\n\t\t\t\t\tcx=\"90\"\n\t\t\t\t\tcy=\"90\"\n\t\t\t\t\tr={r}\n\t\t\t\t\tfill=\"none\"\n\t\t\t\t\tstroke={color}\n\t\t\t\t\tstrokeWidth=\"14\"\n\t\t\t\t\tstrokeDasharray={circ}\n\t\t\t\t\tstrokeDashoffset={offset}\n\t\t\t\t\tstrokeLinecap=\"round\"\n\t\t\t\t\ttransform=\"rotate(-90 90 90)\"\n\t\t\t\t\tstyle={{ transition: \"stroke-dashoffset 0.5s ease\" }}\n\t\t\t\t/>\n\t\t\t\t<text x=\"90\" y=\"85\" textAnchor=\"middle\" fontSize=\"32\" fontWeight=\"bold\" fill={color}>\n\t\t\t\t\t{(score * 100).toFixed(0)}\n\t\t\t\t</text>\n\t\t\t\t<text x=\"90\" y=\"108\" textAnchor=\"middle\" fontSize=\"14\" fill=\"#94a3b8\">/ 100</text>\n\t\t\t</svg>\n\t\t\t<div className=\"health-gauge-label\">Context Health</div>\n\t\t</div>\n\t);\n}\n\nfunction HealthSparkline({ trend }: { trend: number[] }): React.ReactElement {\n\tif (trend.length < 2) return <div className=\"health-spark-empty\">No trend data yet</div>;\n\tconst w = 400;\n\tconst h = 60;\n\tconst max = 1;\n\tconst min = 0;\n\tconst pts = trend.map((v, i) => {\n\t\tconst x = (i / (trend.length - 1)) * w;\n\t\tconst y = h - ((v - min) / (max - min)) * h;\n\t\treturn `${x.toFixed(1)},${y.toFixed(1)}`;\n\t});\n\tconst last = trend[trend.length - 1];\n\treturn (\n\t\t<svg width={w} height={h} viewBox={`0 0 ${w} ${h}`} className=\"health-spark\">\n\t\t\t<polyline points={pts.join(\" \")} fill=\"none\" stroke={healthColor(last)} strokeWidth=\"2\" />\n\t\t</svg>\n\t);\n}\n\nfunction SubScoreBar({ label, value }: { label: string; value: number }): React.ReactElement {\n\treturn (\n\t\t<div className=\"health-subscore\">\n\t\t\t<span className=\"health-subscore-label\">{label}</span>\n\t\t\t<div className=\"health-subscore-bar\">\n\t\t\t\t<div\n\t\t\t\t\tclassName=\"health-subscore-fill\"\n\t\t\t\t\tstyle={{ width: `${value * 100}%`, background: healthColor(value) }}\n\t\t\t\t/>\n\t\t\t</div>\n\t\t\t<span className=\"health-subscore-val\">{(value * 100).toFixed(0)}%</span>\n\t\t</div>\n\t);\n}\n\nexport default function HealthTab(): React.ReactElement {\n\tconst [data, setData] = useState<ContextHealthResponse | null>(null);\n\tconst [err, setError] = useState<string | null>(null);\n\tconst [loading, setLoading] = useState(true);\n\n\tconst poll = useCallback(() => {\n\t\tfetchContextHealth()\n\t\t\t.then(setData)\n\t\t\t.catch((e: unknown) => setError(e instanceof Error ? e.message : String(e)))\n\t\t\t.finally(() => setLoading(false));\n\t}, []);\n\n\tuseEffect(() => {\n\t\tpoll();\n\t\tconst id = setInterval(poll, 5000);\n\t\treturn () => clearInterval(id);\n\t}, [poll]);\n\n\tif (loading && !data) return <div className=\"health-loading\">Loading health data…</div>;\n\tif (err && !data) return <div className=\"health-error\">Error: {err}</div>;\n\tif (!data) return <div className=\"health-empty\">No health data available.</div>;\n\n\tconst latest = data.latest;\n\tconst composite = latest?.composite ?? 0;\n\n\treturn (\n\t\t<div className=\"flex flex-col gap-4\">\n\t\t\t<div className=\"grid grid-cols-1 gap-4 md:grid-cols-2\">\n\t\t\t\t<Card>\n\t\t\t\t\t<CardContent className=\"flex justify-center\">\n\t\t\t\t\t\t<HealthGauge score={composite} />\n\t\t\t\t\t</CardContent>\n\t\t\t\t</Card>\n\t\t\t\t<Card>\n\t\t\t\t\t<CardHeader>\n\t\t\t\t\t\t<CardTitle>Sub-Scores</CardTitle>\n\t\t\t\t\t</CardHeader>\n\t\t\t\t\t<CardContent>\n\t\t\t\t\t\t<SubScoreBar label=\"Drift\" value={latest?.driftScore ?? 0} />\n\t\t\t\t\t\t<SubScoreBar label=\"Output Quality\" value={latest?.outputQuality ?? 0} />\n\t\t\t\t\t\t<SubScoreBar label=\"Error Rate\" value={latest?.errorScore ?? 0} />\n\t\t\t\t\t\t<SubScoreBar label=\"Cache Health\" value={latest?.cacheHealth ?? 0} />\n\t\t\t\t\t\t<SubScoreBar label=\"Cache Poison\" value={latest?.cachePoison ?? 0} />\n\t\t\t\t\t</CardContent>\n\t\t\t\t</Card>\n\t\t\t</div>\n\n\t\t\t<Card>\n\t\t\t\t<CardHeader>\n\t\t\t\t\t<CardTitle>Health Trend (last {data.trend.length} turns)</CardTitle>\n\t\t\t\t</CardHeader>\n\t\t\t\t<CardContent>\n\t\t\t\t\t<HealthSparkline trend={data.trend} />\n\t\t\t\t</CardContent>\n\t\t\t</Card>\n\n\t\t\t{data.alerts.length > 0 && (\n\t\t\t\t<Card>\n\t\t\t\t\t<CardHeader>\n\t\t\t\t\t\t<CardTitle>Recent Alerts ({data.alerts.length})</CardTitle>\n\t\t\t\t\t</CardHeader>\n\t\t\t\t\t<CardContent>\n\t\t\t\t\t\t<div className=\"flex flex-col gap-1\">\n\t\t\t\t\t\t\t{data.alerts.slice(-10).map((a) => (\n\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\tkey={`${a.ts}-${a.turnIndex}`}\n\t\t\t\t\t\t\t\t\tclassName=\"flex items-center gap-3 border-b border-border/50 py-1 text-sm\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<span className=\"text-xs text-muted-foreground\">{new Date(a.ts).toLocaleTimeString()}</span>\n\t\t\t\t\t\t\t\t\t<span className=\"text-muted-foreground\">{a.modelId ?? \"(unknown)\"}</span>\n\t\t\t\t\t\t\t\t\t<span style={{ color: healthColor(a.composite) }}>\n\t\t\t\t\t\t\t\t\t\t{(a.composite * 100).toFixed(0)}\n\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t{a.cachePoison < 0.3 && <Badge variant=\"danger\">CACHE POISON</Badge>}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</CardContent>\n\t\t\t\t</Card>\n\t\t\t)}\n\n\t\t\t{data.perModel.length > 0 && (\n\t\t\t\t<Card>\n\t\t\t\t\t<CardHeader>\n\t\t\t\t\t\t<CardTitle>Health by Model</CardTitle>\n\t\t\t\t\t</CardHeader>\n\t\t\t\t\t<CardContent>\n\t\t\t\t\t\t<table className=\"w-full border-collapse text-sm\">\n\t\t\t\t\t\t\t<thead>\n\t\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t\t\t<th className=\"border-b border-border px-3 py-2 text-left font-medium text-muted-foreground\">Model</th>\n\t\t\t\t\t\t\t\t\t<th className=\"border-b border-border px-3 py-2 text-left font-medium text-muted-foreground\">Avg Health</th>\n\t\t\t\t\t\t\t\t\t<th className=\"border-b border-border px-3 py-2 text-left font-medium text-muted-foreground\">Samples</th>\n\t\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t</thead>\n\t\t\t\t\t\t\t<tbody>\n\t\t\t\t\t\t\t\t{data.perModel.map((m) => (\n\t\t\t\t\t\t\t\t\t<tr key={m.modelId} className=\"border-b border-border/50\">\n\t\t\t\t\t\t\t\t\t\t<td className=\"px-3 py-2\">{m.modelId}</td>\n\t\t\t\t\t\t\t\t\t\t<td className=\"px-3 py-2\" style={{ color: healthColor(m.avgComposite) }}>\n\t\t\t\t\t\t\t\t\t\t\t{(m.avgComposite * 100).toFixed(0)}%\n\t\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t\t<td className=\"px-3 py-2\">{m.sampleCount}</td>\n\t\t\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t</tbody>\n\t\t\t\t\t\t</table>\n\t\t\t\t\t</CardContent>\n\t\t\t\t</Card>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n"],"names":["healthColor","score","HealthGauge","circ","offset","color","jsxs","jsx","HealthSparkline","trend","w","h","max","min","pts","v","i","x","y","last","SubScoreBar","label","value","HealthTab","data","setData","useState","err","setError","loading","setLoading","poll","useCallback","fetchContextHealth","e","useEffect","id","latest","composite","Card","CardContent","CardHeader","CardTitle","a","Badge","m"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{j as e,B as k,r as a,T as E,U as R,_ as C,u as w,V as D,W as A}from"./index-tPP7N38D.js";import{C as p,a as j,b as f,c as y}from"./card-CfCQ36pG.js";import{B as g}from"./button-DyV48WDX.js";import{S as B}from"./switch-7461qYip.js";function T({columns:t}){const l=t.filter(s=>!s.present);return e.jsxs("div",{className:"mt-3",children:[e.jsxs("div",{className:"text-xs font-semibold text-muted-foreground",children:["Column Audit (",t.length," columns, ",l.length," missing)"]}),e.jsxs("table",{className:"mt-1 w-full border-collapse text-sm",children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{className:"border-b border-border px-2 py-1 text-left font-medium text-muted-foreground",children:"Table"}),e.jsx("th",{className:"border-b border-border px-2 py-1 text-left font-medium text-muted-foreground",children:"Column"}),e.jsx("th",{className:"border-b border-border px-2 py-1 text-left font-medium text-muted-foreground",children:"Decl"}),e.jsx("th",{className:"border-b border-border px-2 py-1 text-left font-medium text-muted-foreground",children:"Status"})]})}),e.jsx("tbody",{children:t.map(s=>e.jsxs("tr",{className:"border-b border-border/50",children:[e.jsx("td",{className:"px-2 py-1",children:s.table}),e.jsx("td",{className:"px-2 py-1",children:s.column}),e.jsx("td",{className:"px-2 py-1 font-mono",children:s.expectedDecl}),e.jsx("td",{className:`px-2 py-1 ${s.present?"text-emerald-400":"text-red-400"}`,children:s.present?"✓":"MISSING"})]},`${s.table}.${s.column}`))})]})]})}function L({data:t,loading:l,error:s}){return e.jsxs(p,{children:[e.jsxs(j,{className:"flex-row items-center justify-between space-y-0",children:[e.jsx(f,{children:"Schema Health"}),t&&e.jsx(k,{variant:t.healthy?"success":"danger",children:t.healthy?"HEALTHY":"DEGRADED"})]}),e.jsxs(y,{children:[l&&!t&&e.jsx("span",{className:"text-sm text-muted-foreground",children:"Loading…"}),s&&!t&&e.jsx("span",{className:"text-sm text-red-400",children:s.message}),t&&e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"flex items-baseline justify-between border-b border-border py-1 text-sm",children:[e.jsx("span",{className:"text-muted-foreground",children:"Schema version"}),e.jsx("span",{className:"font-semibold",children:t.schemaVersion})]}),e.jsxs("div",{className:"flex items-baseline justify-between border-b border-border py-1 text-sm",children:[e.jsx("span",{className:"text-muted-foreground",children:"Integrity"}),e.jsx("span",{className:t.integrity[0]==="ok"?"text-emerald-400":"text-red-400",children:t.integrity.join("; ")})]}),e.jsx(T,{columns:t.columns}),t.fkCheck.length>0&&e.jsxs("div",{className:"mt-3",children:[e.jsxs("div",{className:"text-xs font-semibold text-amber-400",children:["FK Check (",t.fkCheck.length,")"]}),t.fkCheck.map((i,d)=>e.jsx("div",{className:"font-mono text-xs text-amber-400",children:i},d))]})]})]})]})}function b(t){return t<1024?`${t} B`:t<1024*1024?`${(t/1024).toFixed(1)} KB`:`${(t/(1024*1024)).toFixed(1)} MB`}function O({files:t,pageSize:l,pageCount:s,freelistPages:i}){const d=t.dbBytes+t.walBytes+t.shmBytes;return e.jsxs("div",{className:"mt-3",children:[e.jsx("div",{className:"text-xs font-semibold text-muted-foreground",children:"File Sizes"}),e.jsx("table",{className:"mt-1 w-full border-collapse text-sm",children:e.jsxs("tbody",{children:[e.jsxs("tr",{className:"border-b border-border/50",children:[e.jsx("td",{className:"px-2 py-1",children:"sqlite.db"}),e.jsx("td",{className:"px-2 py-1 text-right font-semibold",children:b(t.dbBytes)})]}),t.walBytes>0&&e.jsxs("tr",{className:"border-b border-border/50",children:[e.jsx("td",{className:"px-2 py-1",children:".wal"}),e.jsx("td",{className:"px-2 py-1 text-right font-semibold",children:b(t.walBytes)})]}),t.shmBytes>0&&e.jsxs("tr",{className:"border-b border-border/50",children:[e.jsx("td",{className:"px-2 py-1",children:".shm"}),e.jsx("td",{className:"px-2 py-1 text-right font-semibold",children:b(t.shmBytes)})]}),e.jsxs("tr",{className:"border-b border-border/50",children:[e.jsx("td",{className:"px-2 py-1",children:"Total"}),e.jsx("td",{className:"px-2 py-1 text-right font-semibold",children:b(d)})]}),e.jsxs("tr",{className:"border-b border-border/50",children:[e.jsx("td",{className:"px-2 py-1",children:"Page size"}),e.jsx("td",{className:"px-2 py-1 text-right font-semibold",children:b(l)})]}),e.jsxs("tr",{children:[e.jsx("td",{className:"px-2 py-1",children:"Pages"}),e.jsxs("td",{className:"px-2 py-1 text-right font-semibold",children:[s.toLocaleString()," (",i.toLocaleString()," ","free)"]})]})]})})]})}function _({tables:t}){const l=t.reduce((s,i)=>s+Math.max(0,i.rowCount),0);return e.jsxs("div",{className:"mt-3",children:[e.jsxs("div",{className:"text-xs font-semibold text-muted-foreground",children:["Table Row Counts (",l.toLocaleString()," total)"]}),e.jsxs("table",{className:"mt-1 w-full border-collapse text-sm",children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{className:"border-b border-border px-2 py-1 text-left font-medium text-muted-foreground",children:"Table"}),e.jsx("th",{className:"border-b border-border px-2 py-1 text-right font-medium text-muted-foreground",children:"Rows"})]})}),e.jsx("tbody",{children:t.map(s=>e.jsxs("tr",{className:"border-b border-border/50",children:[e.jsx("td",{className:"px-2 py-1",children:s.table}),e.jsx("td",{className:"px-2 py-1 text-right",children:s.rowCount>=0?s.rowCount.toLocaleString():"—"})]},s.table))})]})]})}function M({data:t,loading:l,error:s}){return e.jsxs(p,{children:[e.jsx(j,{children:e.jsx(f,{children:"DB Stats"})}),e.jsxs(y,{children:[l&&!t&&e.jsx("span",{className:"text-sm text-muted-foreground",children:"Loading…"}),s&&!t&&e.jsx("span",{className:"text-sm text-red-400",children:s.message}),t&&e.jsxs(e.Fragment,{children:[e.jsx(O,{files:t.storage.files,pageSize:t.storage.pageSize,pageCount:t.storage.pageCount,freelistPages:t.storage.freelistPages}),e.jsx(_,{tables:t.tables})]})]})]})}const H=[{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 F(){const[t,l]=a.useState([]),[s,i]=a.useState(null),[d,m]=a.useState("30"),u=a.useCallback(async r=>{const h=r.daysOld?{action:"prune",daysOld:Number(d)}:{action:r.key};i(r.key);try{const c=await E(h);l(n=>[{...c,timestamp:Date.now()},...n].slice(0,20))}catch(c){l(n=>[{operation:r.key,success:!1,affected:0,reclaimedBytes:0,summary:c instanceof Error?c.message:String(c),timestamp:Date.now()},...n].slice(0,20))}finally{i(null)}},[d]);return e.jsxs(p,{children:[e.jsx(j,{children:e.jsx(f,{children:"Actions"})}),e.jsx("div",{className:"grid grid-cols-1 gap-3 lg:grid-cols-2",children:H.map(r=>{const h=s===r.key,c=t.find(n=>n.operation===r.key);return e.jsxs("div",{className:"flex items-start justify-between gap-3 rounded-lg border border-border/60 p-3",children:[e.jsxs("div",{className:"min-w-0",children:[e.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[e.jsx("span",{className:"font-semibold",children:r.label}),r.dangerous&&e.jsx(k,{variant:"danger",children:"DESTRUCTIVE"})]}),e.jsx("div",{className:"mt-1 text-xs text-muted-foreground",children:r.desc}),c&&e.jsx("div",{className:`mt-1 text-xs ${c.success?"text-emerald-400":"text-red-400"}`,children:c.summary})]}),e.jsxs("div",{className:"flex shrink-0 items-center gap-2",children:[r.daysOld&&e.jsx("input",{type:"number",min:1,max:365,value:d,onChange:n=>m(n.target.value),className:"w-16 rounded-md border border-border bg-bg-elevated/50 px-2 py-1 text-sm outline-none focus:border-primary",disabled:h}),e.jsx(g,{type:"button",variant:r.dangerous?"ghost":"default",className:r.dangerous?"border border-red-500/50 text-red-400 hover:bg-red-500/10 hover:text-red-300":void 0,disabled:s!==null,onClick:()=>{r.confirm&&!window.confirm(r.confirm)||u(r)},children:h?"Running…":"Run"})]})]},r.key)})})]})}function I(){const[t,l]=a.useState(null),[s,i]=a.useState(!1),[d,m]=a.useState(null),[u,r]=a.useState(!1),h=a.useCallback(async()=>{i(!0),m(null);try{const o=await R();l(o),r(!0)}catch(o){m(o instanceof Error?o.message:String(o))}finally{i(!1)}},[]),c=a.useCallback(()=>{if(!t)return;const o=JSON.stringify(t,null,2);navigator.clipboard.writeText(o).catch(()=>{})},[t]),n=a.useCallback(()=>{if(!t)return;const o=JSON.stringify(t,null,2),S=new Blob([o],{type:"application/json"}),v=URL.createObjectURL(S),N=document.createElement("a");N.href=v,N.download=`mega-compact-debug-bundle-${t.builtAt}.json`,N.click(),URL.revokeObjectURL(v)},[t]),x=t?.criticalEvents.length??0;return e.jsxs(p,{children:[e.jsxs(j,{className:"flex-row items-center justify-between space-y-0",children:[e.jsx(f,{children:"Debug Bundle"}),x>0&&e.jsxs("span",{className:"text-xs text-amber-400",children:[x," critical event",x!==1?"s":""," found"]})]}),e.jsxs(y,{children:[e.jsx("p",{className:"mb-3 text-sm text-muted-foreground",children:"Gather a diagnostic bundle (recent events, config flags, schema health, store stats) to attach to a bug report. Critical/compaction events are highlighted."}),e.jsx(g,{type:"button",variant:"glass",onClick:h,disabled:s,children:s?"Gathering…":"Gather Debug Logs"}),d&&e.jsx("div",{className:"mt-2 text-sm text-red-400",children:d}),t&&u&&e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"mb-2 mt-3 flex gap-2",children:[e.jsx(g,{type:"button",variant:"outline",size:"sm",onClick:c,children:"Copy to clipboard"}),e.jsx(g,{type:"button",variant:"outline",size:"sm",onClick:n,children:"Download JSON"}),e.jsx(g,{type:"button",variant:"outline",size:"sm",onClick:()=>r(o=>!o),children:u?"Hide":"Show"})]}),x>0&&e.jsxs("div",{className:"mt-3",children:[e.jsxs("div",{className:"text-xs font-semibold text-amber-400",children:["Critical events (",x,")"]}),e.jsx("pre",{className:"max-h-48 overflow-auto rounded-md border border-border/60 bg-bg-elevated/40 p-3 font-mono text-xs",children:JSON.stringify(t.criticalEvents,null,2)})]}),e.jsx("pre",{className:"mt-3 max-h-96 overflow-auto rounded-md border border-border/60 bg-bg-elevated/40 p-3 font-mono text-xs",children:JSON.stringify(t,null,2)})]})]})]})}function P(){const[t,l]=a.useState(!1),[s,i]=a.useState(!0),[d,m]=a.useState(!1),[u,r]=a.useState(null),h=a.useCallback(async()=>{try{const{fetchHealthSettings:n}=await C(async()=>{const{fetchHealthSettings:o}=await import("./health-lSeDFTz7.js");return{fetchHealthSettings:o}},[],import.meta.url),x=await n();l(x.mitigate)}catch(n){r(n instanceof Error?n.message:String(n))}finally{i(!1)}},[]);a.useEffect(()=>{h()},[h]);const c=a.useCallback(async()=>{m(!0),r(null);try{const{setHealthMitigate:n}=await C(async()=>{const{setHealthMitigate:o}=await import("./health-lSeDFTz7.js");return{setHealthMitigate:o}},[],import.meta.url),x=!t;await n(x),l(x)}catch(n){r(n instanceof Error?n.message:String(n))}finally{m(!1)}},[t]);return e.jsxs(p,{children:[e.jsx(j,{children:e.jsx(f,{children:"Context Health Mitigation"})}),e.jsx(y,{children:s?e.jsx("p",{className:"text-sm text-muted-foreground",children:"Loading…"}):e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(B,{checked:t,onCheckedChange:c,disabled:d,"aria-label":"Context health auto-mitigation"}),e.jsxs("span",{className:"text-sm",children:["Auto-mitigation ",t?"ON":"OFF"]})]}),e.jsxs("div",{className:"mt-3 space-y-2 text-sm",children:[e.jsxs("p",{children:["When ",e.jsx("strong",{children:"ON"}),", the extension automatically intervenes when context health degrades:"]}),e.jsxs("ul",{className:"list-disc space-y-1 pl-5",children:[e.jsxs("li",{children:[e.jsx("strong",{children:"Forced compaction"})," — when the composite health score drops below 0.4 (severe drift or garbled output), a compaction is triggered to flush the degraded context and rebuild from clean checkpoints, even if the context window isn't full."]}),e.jsxs("li",{children:[e.jsx("strong",{children:"Prefix break"})," — when KV cache poison is detected (score < 0.3), the cached prefix is invalidated to force a cache miss on the next turn, bypassing the corrupted KV state."]})]}),e.jsx("p",{className:"rounded-md border border-amber-500/30 bg-amber-500/5 p-3 text-amber-400",children:"WARNING: both interventions have costs — forced compaction discards live context, and prefix breaks kill cache savings. Only enable when you're seeing degraded output (garbled text, hallucinations, loss of coherence) and the Health tab confirms low scores."}),e.jsxs("p",{className:"text-muted-foreground",children:["Also settable via env var:"," ",e.jsx("code",{children:"MEGACOMPACT_CONTEXT_HEALTH_MITIGATE=1"}),". The dashboard toggle persists to the store and takes precedence."]}),u&&e.jsxs("p",{className:"text-sm text-red-400",children:["Error: ",u]})]})]})})]})}function J(){const{data:t,loading:l,error:s}=w(a.useCallback(()=>D(),[]),{pollInterval:15e3}),{data:i,loading:d,error:m}=w(a.useCallback(()=>A(),[]),{pollInterval:1e4});return e.jsxs("div",{className:"flex flex-col gap-4",children:[e.jsxs("div",{className:"grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-3",children:[e.jsx(L,{data:i,loading:d,error:m}),e.jsx(M,{data:t,loading:l,error:s})]}),e.jsx(F,{}),e.jsx(I,{}),e.jsx(P,{})]})}export{J as default};
|
|
2
|
-
//# sourceMappingURL=MaintenanceTab-
|
|
1
|
+
import{j as e,B as k,r as a,T as E,U as R,_ as C,u as w,V as D,W as A}from"./index-CRHmWnRi.js";import{C as p,a as j,b as f,c as y}from"./card-Cp9Hpdoo.js";import{B as g}from"./button-CoVmU9Vr.js";import{S as B}from"./switch-jrYrJJYy.js";function T({columns:t}){const l=t.filter(s=>!s.present);return e.jsxs("div",{className:"mt-3",children:[e.jsxs("div",{className:"text-xs font-semibold text-muted-foreground",children:["Column Audit (",t.length," columns, ",l.length," missing)"]}),e.jsxs("table",{className:"mt-1 w-full border-collapse text-sm",children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{className:"border-b border-border px-2 py-1 text-left font-medium text-muted-foreground",children:"Table"}),e.jsx("th",{className:"border-b border-border px-2 py-1 text-left font-medium text-muted-foreground",children:"Column"}),e.jsx("th",{className:"border-b border-border px-2 py-1 text-left font-medium text-muted-foreground",children:"Decl"}),e.jsx("th",{className:"border-b border-border px-2 py-1 text-left font-medium text-muted-foreground",children:"Status"})]})}),e.jsx("tbody",{children:t.map(s=>e.jsxs("tr",{className:"border-b border-border/50",children:[e.jsx("td",{className:"px-2 py-1",children:s.table}),e.jsx("td",{className:"px-2 py-1",children:s.column}),e.jsx("td",{className:"px-2 py-1 font-mono",children:s.expectedDecl}),e.jsx("td",{className:`px-2 py-1 ${s.present?"text-emerald-400":"text-red-400"}`,children:s.present?"✓":"MISSING"})]},`${s.table}.${s.column}`))})]})]})}function L({data:t,loading:l,error:s}){return e.jsxs(p,{children:[e.jsxs(j,{className:"flex-row items-center justify-between space-y-0",children:[e.jsx(f,{children:"Schema Health"}),t&&e.jsx(k,{variant:t.healthy?"success":"danger",children:t.healthy?"HEALTHY":"DEGRADED"})]}),e.jsxs(y,{children:[l&&!t&&e.jsx("span",{className:"text-sm text-muted-foreground",children:"Loading…"}),s&&!t&&e.jsx("span",{className:"text-sm text-red-400",children:s.message}),t&&e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"flex items-baseline justify-between border-b border-border py-1 text-sm",children:[e.jsx("span",{className:"text-muted-foreground",children:"Schema version"}),e.jsx("span",{className:"font-semibold",children:t.schemaVersion})]}),e.jsxs("div",{className:"flex items-baseline justify-between border-b border-border py-1 text-sm",children:[e.jsx("span",{className:"text-muted-foreground",children:"Integrity"}),e.jsx("span",{className:t.integrity[0]==="ok"?"text-emerald-400":"text-red-400",children:t.integrity.join("; ")})]}),e.jsx(T,{columns:t.columns}),t.fkCheck.length>0&&e.jsxs("div",{className:"mt-3",children:[e.jsxs("div",{className:"text-xs font-semibold text-amber-400",children:["FK Check (",t.fkCheck.length,")"]}),t.fkCheck.map((i,d)=>e.jsx("div",{className:"font-mono text-xs text-amber-400",children:i},d))]})]})]})]})}function b(t){return t<1024?`${t} B`:t<1024*1024?`${(t/1024).toFixed(1)} KB`:`${(t/(1024*1024)).toFixed(1)} MB`}function O({files:t,pageSize:l,pageCount:s,freelistPages:i}){const d=t.dbBytes+t.walBytes+t.shmBytes;return e.jsxs("div",{className:"mt-3",children:[e.jsx("div",{className:"text-xs font-semibold text-muted-foreground",children:"File Sizes"}),e.jsx("table",{className:"mt-1 w-full border-collapse text-sm",children:e.jsxs("tbody",{children:[e.jsxs("tr",{className:"border-b border-border/50",children:[e.jsx("td",{className:"px-2 py-1",children:"sqlite.db"}),e.jsx("td",{className:"px-2 py-1 text-right font-semibold",children:b(t.dbBytes)})]}),t.walBytes>0&&e.jsxs("tr",{className:"border-b border-border/50",children:[e.jsx("td",{className:"px-2 py-1",children:".wal"}),e.jsx("td",{className:"px-2 py-1 text-right font-semibold",children:b(t.walBytes)})]}),t.shmBytes>0&&e.jsxs("tr",{className:"border-b border-border/50",children:[e.jsx("td",{className:"px-2 py-1",children:".shm"}),e.jsx("td",{className:"px-2 py-1 text-right font-semibold",children:b(t.shmBytes)})]}),e.jsxs("tr",{className:"border-b border-border/50",children:[e.jsx("td",{className:"px-2 py-1",children:"Total"}),e.jsx("td",{className:"px-2 py-1 text-right font-semibold",children:b(d)})]}),e.jsxs("tr",{className:"border-b border-border/50",children:[e.jsx("td",{className:"px-2 py-1",children:"Page size"}),e.jsx("td",{className:"px-2 py-1 text-right font-semibold",children:b(l)})]}),e.jsxs("tr",{children:[e.jsx("td",{className:"px-2 py-1",children:"Pages"}),e.jsxs("td",{className:"px-2 py-1 text-right font-semibold",children:[s.toLocaleString()," (",i.toLocaleString()," ","free)"]})]})]})})]})}function _({tables:t}){const l=t.reduce((s,i)=>s+Math.max(0,i.rowCount),0);return e.jsxs("div",{className:"mt-3",children:[e.jsxs("div",{className:"text-xs font-semibold text-muted-foreground",children:["Table Row Counts (",l.toLocaleString()," total)"]}),e.jsxs("table",{className:"mt-1 w-full border-collapse text-sm",children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{className:"border-b border-border px-2 py-1 text-left font-medium text-muted-foreground",children:"Table"}),e.jsx("th",{className:"border-b border-border px-2 py-1 text-right font-medium text-muted-foreground",children:"Rows"})]})}),e.jsx("tbody",{children:t.map(s=>e.jsxs("tr",{className:"border-b border-border/50",children:[e.jsx("td",{className:"px-2 py-1",children:s.table}),e.jsx("td",{className:"px-2 py-1 text-right",children:s.rowCount>=0?s.rowCount.toLocaleString():"—"})]},s.table))})]})]})}function M({data:t,loading:l,error:s}){return e.jsxs(p,{children:[e.jsx(j,{children:e.jsx(f,{children:"DB Stats"})}),e.jsxs(y,{children:[l&&!t&&e.jsx("span",{className:"text-sm text-muted-foreground",children:"Loading…"}),s&&!t&&e.jsx("span",{className:"text-sm text-red-400",children:s.message}),t&&e.jsxs(e.Fragment,{children:[e.jsx(O,{files:t.storage.files,pageSize:t.storage.pageSize,pageCount:t.storage.pageCount,freelistPages:t.storage.freelistPages}),e.jsx(_,{tables:t.tables})]})]})]})}const H=[{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 F(){const[t,l]=a.useState([]),[s,i]=a.useState(null),[d,m]=a.useState("30"),u=a.useCallback(async r=>{const h=r.daysOld?{action:"prune",daysOld:Number(d)}:{action:r.key};i(r.key);try{const c=await E(h);l(n=>[{...c,timestamp:Date.now()},...n].slice(0,20))}catch(c){l(n=>[{operation:r.key,success:!1,affected:0,reclaimedBytes:0,summary:c instanceof Error?c.message:String(c),timestamp:Date.now()},...n].slice(0,20))}finally{i(null)}},[d]);return e.jsxs(p,{children:[e.jsx(j,{children:e.jsx(f,{children:"Actions"})}),e.jsx("div",{className:"grid grid-cols-1 gap-3 lg:grid-cols-2",children:H.map(r=>{const h=s===r.key,c=t.find(n=>n.operation===r.key);return e.jsxs("div",{className:"flex items-start justify-between gap-3 rounded-lg border border-border/60 p-3",children:[e.jsxs("div",{className:"min-w-0",children:[e.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[e.jsx("span",{className:"font-semibold",children:r.label}),r.dangerous&&e.jsx(k,{variant:"danger",children:"DESTRUCTIVE"})]}),e.jsx("div",{className:"mt-1 text-xs text-muted-foreground",children:r.desc}),c&&e.jsx("div",{className:`mt-1 text-xs ${c.success?"text-emerald-400":"text-red-400"}`,children:c.summary})]}),e.jsxs("div",{className:"flex shrink-0 items-center gap-2",children:[r.daysOld&&e.jsx("input",{type:"number",min:1,max:365,value:d,onChange:n=>m(n.target.value),className:"w-16 rounded-md border border-border bg-bg-elevated/50 px-2 py-1 text-sm outline-none focus:border-primary",disabled:h}),e.jsx(g,{type:"button",variant:r.dangerous?"ghost":"default",className:r.dangerous?"border border-red-500/50 text-red-400 hover:bg-red-500/10 hover:text-red-300":void 0,disabled:s!==null,onClick:()=>{r.confirm&&!window.confirm(r.confirm)||u(r)},children:h?"Running…":"Run"})]})]},r.key)})})]})}function I(){const[t,l]=a.useState(null),[s,i]=a.useState(!1),[d,m]=a.useState(null),[u,r]=a.useState(!1),h=a.useCallback(async()=>{i(!0),m(null);try{const o=await R();l(o),r(!0)}catch(o){m(o instanceof Error?o.message:String(o))}finally{i(!1)}},[]),c=a.useCallback(()=>{if(!t)return;const o=JSON.stringify(t,null,2);navigator.clipboard.writeText(o).catch(()=>{})},[t]),n=a.useCallback(()=>{if(!t)return;const o=JSON.stringify(t,null,2),S=new Blob([o],{type:"application/json"}),v=URL.createObjectURL(S),N=document.createElement("a");N.href=v,N.download=`mega-compact-debug-bundle-${t.builtAt}.json`,N.click(),URL.revokeObjectURL(v)},[t]),x=t?.criticalEvents.length??0;return e.jsxs(p,{children:[e.jsxs(j,{className:"flex-row items-center justify-between space-y-0",children:[e.jsx(f,{children:"Debug Bundle"}),x>0&&e.jsxs("span",{className:"text-xs text-amber-400",children:[x," critical event",x!==1?"s":""," found"]})]}),e.jsxs(y,{children:[e.jsx("p",{className:"mb-3 text-sm text-muted-foreground",children:"Gather a diagnostic bundle (recent events, config flags, schema health, store stats) to attach to a bug report. Critical/compaction events are highlighted."}),e.jsx(g,{type:"button",variant:"glass",onClick:h,disabled:s,children:s?"Gathering…":"Gather Debug Logs"}),d&&e.jsx("div",{className:"mt-2 text-sm text-red-400",children:d}),t&&u&&e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"mb-2 mt-3 flex gap-2",children:[e.jsx(g,{type:"button",variant:"outline",size:"sm",onClick:c,children:"Copy to clipboard"}),e.jsx(g,{type:"button",variant:"outline",size:"sm",onClick:n,children:"Download JSON"}),e.jsx(g,{type:"button",variant:"outline",size:"sm",onClick:()=>r(o=>!o),children:u?"Hide":"Show"})]}),x>0&&e.jsxs("div",{className:"mt-3",children:[e.jsxs("div",{className:"text-xs font-semibold text-amber-400",children:["Critical events (",x,")"]}),e.jsx("pre",{className:"max-h-48 overflow-auto rounded-md border border-border/60 bg-bg-elevated/40 p-3 font-mono text-xs",children:JSON.stringify(t.criticalEvents,null,2)})]}),e.jsx("pre",{className:"mt-3 max-h-96 overflow-auto rounded-md border border-border/60 bg-bg-elevated/40 p-3 font-mono text-xs",children:JSON.stringify(t,null,2)})]})]})]})}function P(){const[t,l]=a.useState(!1),[s,i]=a.useState(!0),[d,m]=a.useState(!1),[u,r]=a.useState(null),h=a.useCallback(async()=>{try{const{fetchHealthSettings:n}=await C(async()=>{const{fetchHealthSettings:o}=await import("./health-lSeDFTz7.js");return{fetchHealthSettings:o}},[],import.meta.url),x=await n();l(x.mitigate)}catch(n){r(n instanceof Error?n.message:String(n))}finally{i(!1)}},[]);a.useEffect(()=>{h()},[h]);const c=a.useCallback(async()=>{m(!0),r(null);try{const{setHealthMitigate:n}=await C(async()=>{const{setHealthMitigate:o}=await import("./health-lSeDFTz7.js");return{setHealthMitigate:o}},[],import.meta.url),x=!t;await n(x),l(x)}catch(n){r(n instanceof Error?n.message:String(n))}finally{m(!1)}},[t]);return e.jsxs(p,{children:[e.jsx(j,{children:e.jsx(f,{children:"Context Health Mitigation"})}),e.jsx(y,{children:s?e.jsx("p",{className:"text-sm text-muted-foreground",children:"Loading…"}):e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(B,{checked:t,onCheckedChange:c,disabled:d,"aria-label":"Context health auto-mitigation"}),e.jsxs("span",{className:"text-sm",children:["Auto-mitigation ",t?"ON":"OFF"]})]}),e.jsxs("div",{className:"mt-3 space-y-2 text-sm",children:[e.jsxs("p",{children:["When ",e.jsx("strong",{children:"ON"}),", the extension automatically intervenes when context health degrades:"]}),e.jsxs("ul",{className:"list-disc space-y-1 pl-5",children:[e.jsxs("li",{children:[e.jsx("strong",{children:"Forced compaction"})," — when the composite health score drops below 0.4 (severe drift or garbled output), a compaction is triggered to flush the degraded context and rebuild from clean checkpoints, even if the context window isn't full."]}),e.jsxs("li",{children:[e.jsx("strong",{children:"Prefix break"})," — when KV cache poison is detected (score < 0.3), the cached prefix is invalidated to force a cache miss on the next turn, bypassing the corrupted KV state."]})]}),e.jsx("p",{className:"rounded-md border border-amber-500/30 bg-amber-500/5 p-3 text-amber-400",children:"WARNING: both interventions have costs — forced compaction discards live context, and prefix breaks kill cache savings. Only enable when you're seeing degraded output (garbled text, hallucinations, loss of coherence) and the Health tab confirms low scores."}),e.jsxs("p",{className:"text-muted-foreground",children:["Also settable via env var:"," ",e.jsx("code",{children:"MEGACOMPACT_CONTEXT_HEALTH_MITIGATE=1"}),". The dashboard toggle persists to the store and takes precedence."]}),u&&e.jsxs("p",{className:"text-sm text-red-400",children:["Error: ",u]})]})]})})]})}function J(){const{data:t,loading:l,error:s}=w(a.useCallback(()=>D(),[]),{pollInterval:15e3}),{data:i,loading:d,error:m}=w(a.useCallback(()=>A(),[]),{pollInterval:1e4});return e.jsxs("div",{className:"flex flex-col gap-4",children:[e.jsxs("div",{className:"grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-3",children:[e.jsx(L,{data:i,loading:d,error:m}),e.jsx(M,{data:t,loading:l,error:s})]}),e.jsx(F,{}),e.jsx(I,{}),e.jsx(P,{})]})}export{J as default};
|
|
2
|
+
//# sourceMappingURL=MaintenanceTab-DgKBZmdD.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"mappings":"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","names":["ColumnAuditCard","columns","missing","c","jsxs","jsx","SchemaHealthCard","data","loading","error","Card","CardHeader","CardTitle","Badge","CardContent","Fragment","line","i","fmtBytes","b","FileSizesCard","files","pageSize","pageCount","freelistPages","total","TableRowCountsCard","tables","totalRows","t","DbStatsCard","ACTIONS","ActionsCard","results","setResults","useState","running","setRunning","days","setDays","handleAction","useCallback","def","action","res","postMaintenanceAction","prev","err","isRunning","lastResult","r","e","Button","DebugBundleCard","bundle","setBundle","setLoading","setError","showJson","setShowJson","gather","fetchDebugBundle","copyToClipboard","text","downloadJson","blob","url","a","criticalCount","v","HealthMitigationCard","mitigate","setMitigate","saving","setSaving","load","fetchHealthSettings","__vitePreload","s","useEffect","toggle","setHealthMitigate","next","Switch","MaintenanceTab","dbStats","statsLoading","statsError","useApi","fetchDbStats","schemaHealth","healthLoading","healthError","fetchSchemaHealth"],"ignoreList":[],"sources":["../../src/tabs/MaintenanceTab/SchemaHealthCard.tsx","../../src/tabs/MaintenanceTab/DbStatsCard.tsx","../../src/tabs/MaintenanceTab/ActionsCard.tsx","../../src/tabs/MaintenanceTab/DebugBundleCard.tsx","../../src/tabs/MaintenanceTab/HealthMitigationCard.tsx","../../src/tabs/MaintenanceTab.tsx"],"sourcesContent":["/**\n * dashboard-client/src/tabs/MaintenanceTab/SchemaHealthCard.tsx — Schema Health card.\n *\n * Extracted from MaintenanceTab.tsx (delegate-shell split). Renders schema\n * version, integrity check, column audit, and FK check from\n * GET /api/maintenance/schema-health.\n */\nimport type React from \"react\";\nimport type { SchemaHealthResponse } from \"@contracts\";\nimport { Badge } from \"../../components/ui/badge\";\nimport {\n\tCard,\n\tCardHeader,\n\tCardTitle,\n\tCardContent,\n} from \"../../components/ui/card\";\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=\"mt-3\">\n\t\t\t<div className=\"text-xs font-semibold text-muted-foreground\">\n\t\t\t\tColumn Audit ({columns.length} columns, {missing.length} missing)\n\t\t\t</div>\n\t\t\t<table className=\"mt-1 w-full border-collapse text-sm\">\n\t\t\t\t<thead>\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<th className=\"border-b border-border px-2 py-1 text-left font-medium text-muted-foreground\">\n\t\t\t\t\t\t\tTable\n\t\t\t\t\t\t</th>\n\t\t\t\t\t\t<th className=\"border-b border-border px-2 py-1 text-left font-medium text-muted-foreground\">\n\t\t\t\t\t\t\tColumn\n\t\t\t\t\t\t</th>\n\t\t\t\t\t\t<th className=\"border-b border-border px-2 py-1 text-left font-medium text-muted-foreground\">\n\t\t\t\t\t\t\tDecl\n\t\t\t\t\t\t</th>\n\t\t\t\t\t\t<th className=\"border-b border-border px-2 py-1 text-left font-medium text-muted-foreground\">\n\t\t\t\t\t\t\tStatus\n\t\t\t\t\t\t</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}`} className=\"border-b border-border/50\">\n\t\t\t\t\t\t\t<td className=\"px-2 py-1\">{c.table}</td>\n\t\t\t\t\t\t\t<td className=\"px-2 py-1\">{c.column}</td>\n\t\t\t\t\t\t\t<td className=\"px-2 py-1 font-mono\">{c.expectedDecl}</td>\n\t\t\t\t\t\t\t<td className={`px-2 py-1 ${c.present ? \"text-emerald-400\" : \"text-red-400\"}`}>\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\nexport function 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<Card>\n\t\t\t<CardHeader className=\"flex-row items-center justify-between space-y-0\">\n\t\t\t\t<CardTitle>Schema Health</CardTitle>\n\t\t\t\t{data && (\n\t\t\t\t\t<Badge variant={data.healthy ? \"success\" : \"danger\"}>\n\t\t\t\t\t\t{data.healthy ? \"HEALTHY\" : \"DEGRADED\"}\n\t\t\t\t\t</Badge>\n\t\t\t\t)}\n\t\t\t</CardHeader>\n\t\t\t<CardContent>\n\t\t\t\t{loading && !data && (\n\t\t\t\t\t<span className=\"text-sm text-muted-foreground\">Loading…</span>\n\t\t\t\t)}\n\t\t\t\t{error && !data && (\n\t\t\t\t\t<span className=\"text-sm text-red-400\">{error.message}</span>\n\t\t\t\t)}\n\t\t\t\t{data && (\n\t\t\t\t\t<>\n\t\t\t\t\t\t<div className=\"flex items-baseline justify-between border-b border-border py-1 text-sm\">\n\t\t\t\t\t\t\t<span className=\"text-muted-foreground\">Schema version</span>\n\t\t\t\t\t\t\t<span className=\"font-semibold\">{data.schemaVersion}</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div className=\"flex items-baseline justify-between border-b border-border py-1 text-sm\">\n\t\t\t\t\t\t\t<span className=\"text-muted-foreground\">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\"\n\t\t\t\t\t\t\t\t\t\t? \"text-emerald-400\"\n\t\t\t\t\t\t\t\t\t\t: \"text-red-400\"\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=\"mt-3\">\n\t\t\t\t\t\t\t\t<div className=\"text-xs font-semibold text-amber-400\">\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=\"font-mono text-xs text-amber-400\">\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</CardContent>\n\t\t</Card>\n\t);\n}\n","/**\n * dashboard-client/src/tabs/MaintenanceTab/DbStatsCard.tsx — DB Stats card.\n *\n * Extracted from MaintenanceTab.tsx (delegate-shell split). Renders file sizes\n * and table row counts from GET /api/maintenance.\n */\nimport type React from \"react\";\nimport type { DbStatsResponse } from \"@contracts\";\nimport {\n\tCard,\n\tCardHeader,\n\tCardTitle,\n\tCardContent,\n} from \"../../components/ui/card\";\n\n// ---------------------------------------------------------------------------\n// Format helpers (local — matches html.ts fmtBytes)\n// ---------------------------------------------------------------------------\n\nexport function 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=\"mt-3\">\n\t\t\t<div className=\"text-xs font-semibold text-muted-foreground\">File Sizes</div>\n\t\t\t<table className=\"mt-1 w-full border-collapse text-sm\">\n\t\t\t\t<tbody>\n\t\t\t\t\t<tr className=\"border-b border-border/50\">\n\t\t\t\t\t\t<td className=\"px-2 py-1\">sqlite.db</td>\n\t\t\t\t\t\t<td className=\"px-2 py-1 text-right font-semibold\">\n\t\t\t\t\t\t\t{fmtBytes(files.dbBytes)}\n\t\t\t\t\t\t</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 className=\"border-b border-border/50\">\n\t\t\t\t\t\t\t<td className=\"px-2 py-1\">.wal</td>\n\t\t\t\t\t\t\t<td className=\"px-2 py-1 text-right font-semibold\">\n\t\t\t\t\t\t\t\t{fmtBytes(files.walBytes)}\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\t{files.shmBytes > 0 && (\n\t\t\t\t\t\t<tr className=\"border-b border-border/50\">\n\t\t\t\t\t\t\t<td className=\"px-2 py-1\">.shm</td>\n\t\t\t\t\t\t\t<td className=\"px-2 py-1 text-right font-semibold\">\n\t\t\t\t\t\t\t\t{fmtBytes(files.shmBytes)}\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\t<tr className=\"border-b border-border/50\">\n\t\t\t\t\t\t<td className=\"px-2 py-1\">Total</td>\n\t\t\t\t\t\t<td className=\"px-2 py-1 text-right font-semibold\">\n\t\t\t\t\t\t\t{fmtBytes(total)}\n\t\t\t\t\t\t</td>\n\t\t\t\t\t</tr>\n\t\t\t\t\t<tr className=\"border-b border-border/50\">\n\t\t\t\t\t\t<td className=\"px-2 py-1\">Page size</td>\n\t\t\t\t\t\t<td className=\"px-2 py-1 text-right font-semibold\">\n\t\t\t\t\t\t\t{fmtBytes(pageSize)}\n\t\t\t\t\t\t</td>\n\t\t\t\t\t</tr>\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<td className=\"px-2 py-1\">Pages</td>\n\t\t\t\t\t\t<td className=\"px-2 py-1 text-right font-semibold\">\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=\"mt-3\">\n\t\t\t<div className=\"text-xs font-semibold text-muted-foreground\">\n\t\t\t\tTable Row Counts ({totalRows.toLocaleString()} total)\n\t\t\t</div>\n\t\t\t<table className=\"mt-1 w-full border-collapse text-sm\">\n\t\t\t\t<thead>\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<th className=\"border-b border-border px-2 py-1 text-left font-medium text-muted-foreground\">\n\t\t\t\t\t\t\tTable\n\t\t\t\t\t\t</th>\n\t\t\t\t\t\t<th className=\"border-b border-border px-2 py-1 text-right font-medium text-muted-foreground\">\n\t\t\t\t\t\t\tRows\n\t\t\t\t\t\t</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} className=\"border-b border-border/50\">\n\t\t\t\t\t\t\t<td className=\"px-2 py-1\">{t.table}</td>\n\t\t\t\t\t\t\t<td className=\"px-2 py-1 text-right\">\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\nexport function 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<Card>\n\t\t\t<CardHeader>\n\t\t\t\t<CardTitle>DB Stats</CardTitle>\n\t\t\t</CardHeader>\n\t\t\t<CardContent>\n\t\t\t\t{loading && !data && (\n\t\t\t\t\t<span className=\"text-sm text-muted-foreground\">Loading…</span>\n\t\t\t\t)}\n\t\t\t\t{error && !data && (\n\t\t\t\t\t<span className=\"text-sm text-red-400\">{error.message}</span>\n\t\t\t\t)}\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</CardContent>\n\t\t</Card>\n\t);\n}\n","/**\n * dashboard-client/src/tabs/MaintenanceTab/ActionsCard.tsx — Actions card.\n *\n * Extracted from MaintenanceTab.tsx (delegate-shell split). One-time\n * maintenance operations (vacuum, checkpoint, reindex, prune, …) with a\n * results history.\n */\nimport type React from \"react\";\nimport { useState, useCallback } from \"react\";\nimport type {\n\tMaintenanceAction,\n\tMaintenanceActionResult,\n} from \"@contracts\";\nimport { postMaintenanceAction } from \"../../api/client\";\nimport { Badge } from \"../../components/ui/badge\";\nimport { Button } from \"../../components/ui/button\";\nimport { Card, CardHeader, CardTitle } from \"../../components/ui/card\";\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// Actions card\n// ---------------------------------------------------------------------------\n\nexport function 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<Card>\n\t\t\t<CardHeader>\n\t\t\t\t<CardTitle>Actions</CardTitle>\n\t\t\t</CardHeader>\n\t\t\t<div className=\"grid grid-cols-1 gap-3 lg:grid-cols-2\">\n\t\t\t\t{ACTIONS.map((def) => {\n\t\t\t\t\tconst isRunning = running === def.key;\n\t\t\t\t\tconst lastResult = results.find((r) => r.operation === def.key);\n\t\t\t\t\treturn (\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tkey={def.key}\n\t\t\t\t\t\t\tclassName=\"flex items-start justify-between gap-3 rounded-lg border border-border/60 p-3\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<div className=\"min-w-0\">\n\t\t\t\t\t\t\t\t<div className=\"flex flex-wrap items-center gap-2\">\n\t\t\t\t\t\t\t\t\t<span className=\"font-semibold\">{def.label}</span>\n\t\t\t\t\t\t\t\t\t{def.dangerous && <Badge variant=\"danger\">DESTRUCTIVE</Badge>}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t<div className=\"mt-1 text-xs text-muted-foreground\">{def.desc}</div>\n\t\t\t\t\t\t\t\t{lastResult && (\n\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\tclassName={`mt-1 text-xs ${lastResult.success ? \"text-emerald-400\" : \"text-red-400\"}`}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{lastResult.summary}\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\t<div className=\"flex shrink-0 items-center gap-2\">\n\t\t\t\t\t\t\t\t{def.daysOld && (\n\t\t\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\t\t\ttype=\"number\"\n\t\t\t\t\t\t\t\t\t\tmin={1}\n\t\t\t\t\t\t\t\t\t\tmax={365}\n\t\t\t\t\t\t\t\t\t\tvalue={days}\n\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\tclassName=\"w-16 rounded-md border border-border bg-bg-elevated/50 px-2 py-1 text-sm outline-none focus:border-primary\"\n\t\t\t\t\t\t\t\t\t\tdisabled={isRunning}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tvariant={def.dangerous ? \"ghost\" : \"default\"}\n\t\t\t\t\t\t\t\t\tclassName={\n\t\t\t\t\t\t\t\t\t\tdef.dangerous\n\t\t\t\t\t\t\t\t\t\t\t? \"border border-red-500/50 text-red-400 hover:bg-red-500/10 hover:text-red-300\"\n\t\t\t\t\t\t\t\t\t\t\t: undefined\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tdisabled={running !== null}\n\t\t\t\t\t\t\t\t\tonClick={() => {\n\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\thandleAction(def);\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{isRunning ? \"Running…\" : \"Run\"}\n\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t);\n\t\t\t\t})}\n\t\t\t</div>\n\t\t</Card>\n\t);\n}\n","/**\n * dashboard-client/src/tabs/MaintenanceTab/DebugBundleCard.tsx — Debug Bundle card.\n *\n * Extracted from MaintenanceTab.tsx (delegate-shell split). Gather a diagnostic\n * bundle (events, config, schema health, store stats) and let the user copy or\n * download it for bug reports.\n */\nimport type React from \"react\";\nimport { useState, useCallback } from \"react\";\nimport type { DebugBundleResponse } from \"@contracts\";\nimport { fetchDebugBundle } from \"../../api/client\";\nimport { Button } from \"../../components/ui/button\";\nimport {\n\tCard,\n\tCardHeader,\n\tCardTitle,\n\tCardContent,\n} from \"../../components/ui/card\";\n\n// ---------------------------------------------------------------------------\n// Debug bundle card — gather diagnostic info for bug reports\n// ---------------------------------------------------------------------------\n\nexport function DebugBundleCard(): React.ReactElement {\n\tconst [bundle, setBundle] = useState<DebugBundleResponse | null>(null);\n\tconst [loading, setLoading] = useState(false);\n\tconst [error, setError] = useState<string | null>(null);\n\tconst [showJson, setShowJson] = useState(false);\n\n\tconst gather = useCallback(async () => {\n\t\tsetLoading(true);\n\t\tsetError(null);\n\t\ttry {\n\t\t\tconst res = await fetchDebugBundle();\n\t\t\tsetBundle(res);\n\t\t\tsetShowJson(true);\n\t\t} catch (err) {\n\t\t\tsetError(err instanceof Error ? err.message : String(err));\n\t\t} finally {\n\t\t\tsetLoading(false);\n\t\t}\n\t}, []);\n\n\tconst copyToClipboard = useCallback(() => {\n\t\tif (!bundle) return;\n\t\tconst text = JSON.stringify(bundle, null, 2);\n\t\tvoid navigator.clipboard.writeText(text).catch(() => {});\n\t}, [bundle]);\n\n\tconst downloadJson = useCallback(() => {\n\t\tif (!bundle) return;\n\t\tconst text = JSON.stringify(bundle, null, 2);\n\t\tconst blob = new Blob([text], { type: \"application/json\" });\n\t\tconst url = URL.createObjectURL(blob);\n\t\tconst a = document.createElement(\"a\");\n\t\ta.href = url;\n\t\ta.download = `mega-compact-debug-bundle-${bundle.builtAt}.json`;\n\t\ta.click();\n\t\tURL.revokeObjectURL(url);\n\t}, [bundle]);\n\n\tconst criticalCount = bundle?.criticalEvents.length ?? 0;\n\n\treturn (\n\t\t<Card>\n\t\t\t<CardHeader className=\"flex-row items-center justify-between space-y-0\">\n\t\t\t\t<CardTitle>Debug Bundle</CardTitle>\n\t\t\t\t{criticalCount > 0 && (\n\t\t\t\t\t<span className=\"text-xs text-amber-400\">\n\t\t\t\t\t\t{criticalCount} critical event{criticalCount !== 1 ? \"s\" : \"\"} found\n\t\t\t\t\t</span>\n\t\t\t\t)}\n\t\t\t</CardHeader>\n\t\t\t<CardContent>\n\t\t\t\t<p className=\"mb-3 text-sm text-muted-foreground\">\n\t\t\t\t\tGather a diagnostic bundle (recent events, config flags, schema health,\n\t\t\t\t\tstore stats) to attach to a bug report. Critical/compaction events are\n\t\t\t\t\thighlighted.\n\t\t\t\t</p>\n\t\t\t\t<Button type=\"button\" variant=\"glass\" onClick={gather} disabled={loading}>\n\t\t\t\t\t{loading ? \"Gathering…\" : \"Gather Debug Logs\"}\n\t\t\t\t</Button>\n\t\t\t\t{error && <div className=\"mt-2 text-sm text-red-400\">{error}</div>}\n\t\t\t\t{bundle && showJson && (\n\t\t\t\t\t<>\n\t\t\t\t\t\t<div className=\"mb-2 mt-3 flex gap-2\">\n\t\t\t\t\t\t\t<Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={copyToClipboard}>\n\t\t\t\t\t\t\t\tCopy to clipboard\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t<Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={downloadJson}>\n\t\t\t\t\t\t\t\tDownload JSON\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\t\t\tonClick={() => setShowJson((v) => !v)}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{showJson ? \"Hide\" : \"Show\"}\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t{criticalCount > 0 && (\n\t\t\t\t\t\t\t<div className=\"mt-3\">\n\t\t\t\t\t\t\t\t<div className=\"text-xs font-semibold text-amber-400\">\n\t\t\t\t\t\t\t\t\tCritical events ({criticalCount})\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t<pre className=\"max-h-48 overflow-auto rounded-md border border-border/60 bg-bg-elevated/40 p-3 font-mono text-xs\">\n\t\t\t\t\t\t\t\t\t{JSON.stringify(bundle.criticalEvents, null, 2)}\n\t\t\t\t\t\t\t\t</pre>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t)}\n\t\t\t\t\t\t<pre className=\"mt-3 max-h-96 overflow-auto rounded-md border border-border/60 bg-bg-elevated/40 p-3 font-mono text-xs\">\n\t\t\t\t\t\t\t{JSON.stringify(bundle, null, 2)}\n\t\t\t\t\t\t</pre>\n\t\t\t\t\t</>\n\t\t\t\t)}\n\t\t\t</CardContent>\n\t\t</Card>\n\t);\n}\n","/**\n * dashboard-client/src/tabs/MaintenanceTab/HealthMitigationCard.tsx — Health\n * Mitigation card.\n *\n * Extracted from MaintenanceTab.tsx (delegate-shell split). Toggles auto-\n * mitigation for degraded context (forced compaction / prefix break).\n */\nimport type React from \"react\";\nimport { useState, useCallback, useEffect } from \"react\";\nimport { Switch } from \"../../components/ui/switch\";\nimport {\n\tCard,\n\tCardHeader,\n\tCardTitle,\n\tCardContent,\n} from \"../../components/ui/card\";\n\n// ---------------------------------------------------------------------------\n// Health Mitigation card — toggle auto-mitigation for degraded context\n// ---------------------------------------------------------------------------\n\nexport function HealthMitigationCard(): React.ReactElement {\n\tconst [mitigate, setMitigate] = useState(false);\n\tconst [loading, setLoading] = useState(true);\n\tconst [saving, setSaving] = useState(false);\n\tconst [error, setError] = useState<string | null>(null);\n\n\tconst load = useCallback(async () => {\n\t\ttry {\n\t\t\tconst { fetchHealthSettings } = await import(\"../../api/health\");\n\t\t\tconst s = await fetchHealthSettings();\n\t\t\tsetMitigate(s.mitigate);\n\t\t} catch (e) {\n\t\t\tsetError(e instanceof Error ? e.message : String(e));\n\t\t} finally {\n\t\t\tsetLoading(false);\n\t\t}\n\t}, []);\n\n\tuseEffect(() => { void load(); }, [load]);\n\n\tconst toggle = useCallback(async () => {\n\t\tsetSaving(true);\n\t\tsetError(null);\n\t\ttry {\n\t\t\tconst { setHealthMitigate } = await import(\"../../api/health\");\n\t\t\tconst next = !mitigate;\n\t\t\tawait setHealthMitigate(next);\n\t\t\tsetMitigate(next);\n\t\t} catch (e) {\n\t\t\tsetError(e instanceof Error ? e.message : String(e));\n\t\t} finally {\n\t\t\tsetSaving(false);\n\t\t}\n\t}, [mitigate]);\n\n\treturn (\n\t\t<Card>\n\t\t\t<CardHeader>\n\t\t\t\t<CardTitle>Context Health Mitigation</CardTitle>\n\t\t\t</CardHeader>\n\t\t\t<CardContent>\n\t\t\t\t{loading ? (\n\t\t\t\t\t<p className=\"text-sm text-muted-foreground\">Loading…</p>\n\t\t\t\t) : (\n\t\t\t\t\t<>\n\t\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t<Switch\n\t\t\t\t\t\t\t\tchecked={mitigate}\n\t\t\t\t\t\t\t\tonCheckedChange={toggle}\n\t\t\t\t\t\t\t\tdisabled={saving}\n\t\t\t\t\t\t\t\taria-label=\"Context health auto-mitigation\"\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<span className=\"text-sm\">\n\t\t\t\t\t\t\t\tAuto-mitigation {mitigate ? \"ON\" : \"OFF\"}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div className=\"mt-3 space-y-2 text-sm\">\n\t\t\t\t\t\t\t<p>\n\t\t\t\t\t\t\t\tWhen <strong>ON</strong>, the extension automatically intervenes\n\t\t\t\t\t\t\t\twhen context health degrades:\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t<ul className=\"list-disc space-y-1 pl-5\">\n\t\t\t\t\t\t\t\t<li>\n\t\t\t\t\t\t\t\t\t<strong>Forced compaction</strong> — when the composite health\n\t\t\t\t\t\t\t\t\tscore drops below 0.4 (severe drift or garbled output), a\n\t\t\t\t\t\t\t\t\tcompaction is triggered to flush the degraded context and\n\t\t\t\t\t\t\t\t\trebuild from clean checkpoints, even if the context window\n\t\t\t\t\t\t\t\t\tisn't full.\n\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t\t<li>\n\t\t\t\t\t\t\t\t\t<strong>Prefix break</strong> — when KV cache poison is\n\t\t\t\t\t\t\t\t\tdetected (score < 0.3), the cached prefix is invalidated to\n\t\t\t\t\t\t\t\t\tforce a cache miss on the next turn, bypassing the corrupted KV\n\t\t\t\t\t\t\t\t\tstate.\n\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t</ul>\n\t\t\t\t\t\t\t<p className=\"rounded-md border border-amber-500/30 bg-amber-500/5 p-3 text-amber-400\">\n\t\t\t\t\t\t\t\tWARNING: both interventions have costs — forced compaction discards\n\t\t\t\t\t\t\t\tlive context, and prefix breaks kill cache savings. Only enable\n\t\t\t\t\t\t\t\twhen you're seeing degraded output (garbled text,\n\t\t\t\t\t\t\t\thallucinations, loss of coherence) and the Health tab confirms low\n\t\t\t\t\t\t\t\tscores.\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t<p className=\"text-muted-foreground\">\n\t\t\t\t\t\t\t\tAlso settable via env var:{\" \"}\n\t\t\t\t\t\t\t\t<code>MEGACOMPACT_CONTEXT_HEALTH_MITIGATE=1</code>. The dashboard\n\t\t\t\t\t\t\t\ttoggle persists to the store and takes precedence.\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t{error && <p className=\"text-sm text-red-400\">Error: {error}</p>}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</>\n\t\t\t\t)}\n\t\t\t</CardContent>\n\t\t</Card>\n\t);\n}\n","/**\n * dashboard-client/src/tabs/MaintenanceTab.tsx — Maintenance tab (v0.11.2 S49B).\n *\n * Delegate-shell (extensions split): three cards (Schema Health, DB Stats,\n * Actions) plus the Debug Bundle and Health Mitigation cards. The heavy card\n * components live in ./MaintenanceTab/* to keep every file under the extensions\n * line limit. All cards fetch independently; failures in one do not block the\n * others.\n */\n\nimport type React from \"react\";\nimport { useCallback } from \"react\";\nimport type {\n\tDbStatsResponse,\n\tSchemaHealthResponse,\n} from \"@contracts\";\nimport { useApi } from \"../hooks/useApi\";\nimport { fetchDbStats, fetchSchemaHealth } from \"../api/client\";\nimport { SchemaHealthCard } from \"./MaintenanceTab/SchemaHealthCard\";\nimport { DbStatsCard } from \"./MaintenanceTab/DbStatsCard\";\nimport { ActionsCard } from \"./MaintenanceTab/ActionsCard\";\nimport { DebugBundleCard } from \"./MaintenanceTab/DebugBundleCard\";\nimport { HealthMitigationCard } from \"./MaintenanceTab/HealthMitigationCard\";\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=\"flex flex-col gap-4\">\n\t\t\t<div className=\"grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-3\">\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\t<DebugBundleCard />\n\t\t\t<HealthMitigationCard />\n\t\t</div>\n\t);\n}\n"],"file":"assets/MaintenanceTab-DxIQyeh-.js"}
|
|
1
|
+
{"version":3,"mappings":"8OAqBA,SAASA,EAAgB,CACxB,QAAAC,CACD,EAEuB,CACtB,MAAMC,EAAUD,EAAQ,OAAQE,GAAM,CAACA,EAAE,OAAO,EAChD,OACCC,OAAC,OAAI,UAAU,OACd,UAAAA,OAAC,OAAI,UAAU,8CAA8C,2BAC7CH,EAAQ,OAAO,aAAWC,EAAQ,OAAO,aACzD,EACAE,OAAC,SAAM,UAAU,sCAChB,UAAAC,MAAC,SACA,gBAAC,MACA,UAAAA,MAAC,MAAG,UAAU,+EAA+E,iBAE7F,EACAA,MAAC,MAAG,UAAU,+EAA+E,kBAE7F,EACAA,MAAC,MAAG,UAAU,+EAA+E,gBAE7F,EACAA,MAAC,MAAG,UAAU,+EAA+E,kBAE7F,GACD,EACD,EACAA,MAAC,SACC,SAAAJ,EAAQ,IAAKE,GACbC,OAAC,MAAkC,UAAU,4BAC5C,UAAAC,MAAC,MAAG,UAAU,YAAa,SAAAF,EAAE,MAAM,EACnCE,MAAC,MAAG,UAAU,YAAa,WAAE,OAAO,EACpCA,MAAC,MAAG,UAAU,sBAAuB,WAAE,aAAa,EACpDA,MAAC,MAAG,UAAW,aAAaF,EAAE,QAAU,mBAAqB,cAAc,GACzE,SAAAA,EAAE,QAAU,IAAM,UACpB,IANQ,GAAGA,EAAE,KAAK,IAAIA,EAAE,MAAM,EAO/B,CACA,EACF,GACD,GACD,CAEF,CAEO,SAASG,EAAiB,CAChC,KAAAC,EACA,QAAAC,EACA,MAAAC,CACD,EAIuB,CACtB,cACEC,EAAA,CACA,UAAAN,OAACO,EAAA,CAAW,UAAU,kDACrB,UAAAN,MAACO,GAAU,yBAAa,EACvBL,GACAF,MAACQ,EAAA,CAAM,QAASN,EAAK,QAAU,UAAY,SACzC,SAAAA,EAAK,QAAU,UAAY,WAC7B,GAEF,SACCO,EAAA,CACC,UAAAN,GAAW,CAACD,GACZF,MAAC,QAAK,UAAU,gCAAgC,oBAAQ,EAExDI,GAAS,CAACF,GACVF,MAAC,QAAK,UAAU,uBAAwB,WAAM,QAAQ,EAEtDE,GACAH,OAAAW,WAAA,CACC,UAAAX,OAAC,OAAI,UAAU,0EACd,UAAAC,MAAC,QAAK,UAAU,wBAAwB,0BAAc,EACtDA,MAAC,QAAK,UAAU,gBAAiB,WAAK,cAAc,GACrD,EACAD,OAAC,OAAI,UAAU,0EACd,UAAAC,MAAC,QAAK,UAAU,wBAAwB,qBAAS,EACjDA,MAAC,QACA,UACCE,EAAK,UAAU,CAAC,IAAM,KACnB,mBACA,eAGH,SAAAA,EAAK,UAAU,KAAK,IAAI,GAC1B,EACD,EACAF,MAACL,EAAA,CAAgB,QAASO,EAAK,QAAS,EACvCA,EAAK,QAAQ,OAAS,GACtBH,OAAC,OAAI,UAAU,OACd,UAAAA,OAAC,OAAI,UAAU,uCAAuC,uBAC1CG,EAAK,QAAQ,OAAO,KAChC,EACCA,EAAK,QAAQ,IAAI,CAACS,EAAMC,IACxBZ,MAAC,OAAY,UAAU,mCACrB,SAAAW,CAAA,EADQC,CAEV,CACA,GACF,GAEF,GAEF,GACD,CAEF,CC7GO,SAASC,EAASC,EAAmB,CAC3C,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,OACCjB,OAAC,OAAI,UAAU,OACd,UAAAC,MAAC,OAAI,UAAU,8CAA8C,sBAAU,EACvEA,MAAC,SAAM,UAAU,sCAChB,gBAAC,SACA,UAAAD,OAAC,MAAG,UAAU,4BACb,UAAAC,MAAC,MAAG,UAAU,YAAY,qBAAS,QAClC,MAAG,UAAU,qCACZ,SAAAa,EAASG,EAAM,OAAO,EACxB,GACD,EACCA,EAAM,SAAW,GACjBjB,OAAC,MAAG,UAAU,4BACb,UAAAC,MAAC,MAAG,UAAU,YAAY,gBAAI,QAC7B,MAAG,UAAU,qCACZ,SAAAa,EAASG,EAAM,QAAQ,EACzB,GACD,EAEAA,EAAM,SAAW,GACjBjB,OAAC,MAAG,UAAU,4BACb,UAAAC,MAAC,MAAG,UAAU,YAAY,gBAAI,QAC7B,MAAG,UAAU,qCACZ,SAAAa,EAASG,EAAM,QAAQ,EACzB,GACD,EAEDjB,OAAC,MAAG,UAAU,4BACb,UAAAC,MAAC,MAAG,UAAU,YAAY,iBAAK,QAC9B,MAAG,UAAU,qCACZ,SAAAa,EAASO,CAAK,EAChB,GACD,EACArB,OAAC,MAAG,UAAU,4BACb,UAAAC,MAAC,MAAG,UAAU,YAAY,qBAAS,QAClC,MAAG,UAAU,qCACZ,SAAAa,EAASI,CAAQ,EACnB,GACD,SACC,MACA,UAAAjB,MAAC,MAAG,UAAU,YAAY,iBAAK,EAC/BD,OAAC,MAAG,UAAU,qCACZ,UAAAmB,EAAU,iBAAiB,KAAGC,EAAc,iBAAkB,IAAI,SAEpE,GACD,GACD,EACD,GACD,CAEF,CAEA,SAASE,EAAmB,CAC3B,OAAAC,CACD,EAEuB,CACtB,MAAMC,EAAYD,EAAO,OAAO,CAAC,EAAGE,IAAM,EAAI,KAAK,IAAI,EAAGA,EAAE,QAAQ,EAAG,CAAC,EACxE,OACCzB,OAAC,OAAI,UAAU,OACd,UAAAA,OAAC,OAAI,UAAU,8CAA8C,+BACzCwB,EAAU,iBAAiB,WAC/C,EACAxB,OAAC,SAAM,UAAU,sCAChB,UAAAC,MAAC,SACA,gBAAC,MACA,UAAAA,MAAC,MAAG,UAAU,+EAA+E,iBAE7F,EACAA,MAAC,MAAG,UAAU,gFAAgF,gBAE9F,GACD,EACD,EACAA,MAAC,SACC,SAAAsB,EAAO,IAAKE,GACZzB,OAAC,MAAiB,UAAU,4BAC3B,UAAAC,MAAC,MAAG,UAAU,YAAa,SAAAwB,EAAE,MAAM,EACnCxB,MAAC,MAAG,UAAU,uBACZ,SAAAwB,EAAE,UAAY,EAAIA,EAAE,SAAS,iBAAmB,IAClD,IAJQA,EAAE,KAKX,CACA,EACF,GACD,GACD,CAEF,CAEO,SAASC,EAAY,CAC3B,KAAAvB,EACA,QAAAC,EACA,MAAAC,CACD,EAIuB,CACtB,cACEC,EAAA,CACA,UAAAL,MAACM,EAAA,CACA,SAAAN,MAACO,EAAA,CAAU,oBAAQ,EACpB,SACCE,EAAA,CACC,UAAAN,GAAW,CAACD,GACZF,MAAC,QAAK,UAAU,gCAAgC,oBAAQ,EAExDI,GAAS,CAACF,GACVF,MAAC,QAAK,UAAU,uBAAwB,WAAM,QAAQ,EAEtDE,GACAH,OAAAW,WAAA,CACC,UAAAV,MAACe,EAAA,CACA,MAAOb,EAAK,QAAQ,MACpB,SAAUA,EAAK,QAAQ,SACvB,UAAWA,EAAK,QAAQ,UACxB,cAAeA,EAAK,QAAQ,gBAE7BF,MAACqB,EAAA,CAAmB,OAAQnB,EAAK,OAAQ,GAC1C,GAEF,GACD,CAEF,CCtIA,MAAMwB,EAAuB,CAC5B,CACC,IAAK,kBACL,MAAO,kBACP,KAAM,6BACN,UAAW,IAEZ,CACC,IAAK,kBACL,MAAO,yBACP,KAAM,wCACN,UAAW,GACX,QAAS,6CAEV,CACC,IAAK,aACL,MAAO,iBACP,KAAM,qCACN,UAAW,IAEZ,CACC,IAAK,SACL,MAAO,SACP,KAAM,mCACN,UAAW,GACX,QAAS,4CAEV,CACC,IAAK,UACL,MAAO,UACP,KAAM,sBACN,UAAW,GACX,QAAS,uCAEV,CACC,IAAK,eACL,MAAO,eACP,KAAM,iCACN,UAAW,GACX,QAAS,yCAEV,CACC,IAAK,QACL,MAAO,iBACP,KAAM,gDACN,UAAW,GACX,QAAS,GACT,QAAS,4CAEX,EAMO,SAASC,GAAkC,CACjD,KAAM,CAACC,EAASC,CAAU,EAAIC,WAAoC,EAAE,EAC9D,CAACC,EAASC,CAAU,EAAIF,WAAwB,IAAI,EACpD,CAACG,EAAMC,CAAO,EAAIJ,WAAS,IAAI,EAE/BK,EAAeC,cACpB,MAAOC,GAAmB,CACzB,MAAMC,EAA4BD,EAAI,QACnC,CAAE,OAAQ,QAAS,QAAS,OAAOJ,CAAI,GACtC,CAAE,OAAQI,EAAI,KAElBL,EAAWK,EAAI,GAAG,EAClB,GAAI,CACH,MAAME,EAAM,MAAMC,EAAsBF,CAAM,EAC9CT,EAAYY,GACX,CAAC,CAAE,GAAGF,EAAK,UAAW,KAAK,KAAI,EAAK,GAAGE,CAAI,EAAE,MAAM,EAAG,EAAE,EAE1D,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,KAAI,EAErB,GAAGD,CAAA,EACF,MAAM,EAAG,EAAE,EAEf,SACCT,EAAW,IAAI,CAChB,CACD,EACA,CAACC,CAAI,GAGN,cACE5B,EAAA,CACA,UAAAL,MAACM,EAAA,CACA,SAAAN,MAACO,EAAA,CAAU,mBAAO,EACnB,QACC,OAAI,UAAU,wCACb,SAAAmB,EAAQ,IAAKW,GAAQ,CACrB,MAAMM,EAAYZ,IAAYM,EAAI,IAC5BO,EAAahB,EAAQ,KAAMiB,GAAMA,EAAE,YAAcR,EAAI,GAAG,EAC9D,OACCtC,OAAC,OAEA,UAAU,gFAEV,UAAAA,OAAC,OAAI,UAAU,UACd,UAAAA,OAAC,OAAI,UAAU,oCACd,UAAAC,MAAC,QAAK,UAAU,gBAAiB,SAAAqC,EAAI,MAAM,EAC1CA,EAAI,WAAarC,MAACQ,EAAA,CAAM,QAAQ,SAAS,uBAAW,GACtD,EACAR,MAAC,OAAI,UAAU,qCAAsC,WAAI,KAAK,EAC7D4C,GACA5C,MAAC,OACA,UAAW,gBAAgB4C,EAAW,QAAU,mBAAqB,cAAc,GAElF,SAAAA,EAAW,SACb,EAEF,EACA7C,OAAC,OAAI,UAAU,mCACb,UAAAsC,EAAI,SACJrC,MAAC,SACA,KAAK,SACL,IAAK,EACL,IAAK,IACL,MAAOiC,EACP,SAAWa,GAAMZ,EAAQY,EAAE,OAAO,KAAK,EACvC,UAAU,6GACV,SAAUH,CAAA,GAGZ3C,MAAC+C,EAAA,CACA,KAAK,SACL,QAASV,EAAI,UAAY,QAAU,UACnC,UACCA,EAAI,UACD,+EACA,OAEJ,SAAUN,IAAY,KACtB,QAAS,IAAM,CACVM,EAAI,SAAW,CAAC,OAAO,QAAQA,EAAI,OAAO,GAC9CF,EAAaE,CAAG,CACjB,EAEC,WAAY,WAAa,OAC3B,EACD,IA7CKA,EAAI,IAgDZ,CAAC,EACF,GACD,CAEF,CCpKO,SAASW,GAAsC,CACrD,KAAM,CAACC,EAAQC,CAAS,EAAIpB,WAAqC,IAAI,EAC/D,CAAC3B,EAASgD,CAAU,EAAIrB,WAAS,EAAK,EACtC,CAAC1B,EAAOgD,CAAQ,EAAItB,WAAwB,IAAI,EAChD,CAACuB,EAAUC,CAAW,EAAIxB,WAAS,EAAK,EAExCyB,EAASnB,cAAY,SAAY,CACtCe,EAAW,EAAI,EACfC,EAAS,IAAI,EACb,GAAI,CACH,MAAMb,EAAM,MAAMiB,EAAA,EAClBN,EAAUX,CAAG,EACbe,EAAY,EAAI,CACjB,OAASZ,EAAK,CACbU,EAASV,aAAe,MAAQA,EAAI,QAAU,OAAOA,CAAG,CAAC,CAC1D,SACCS,EAAW,EAAK,CACjB,CACD,EAAG,EAAE,EAECM,EAAkBrB,cAAY,IAAM,CACzC,GAAI,CAACa,EAAQ,OACb,MAAMS,EAAO,KAAK,UAAUT,EAAQ,KAAM,CAAC,EACtC,UAAU,UAAU,UAAUS,CAAI,EAAE,MAAM,IAAM,CAAC,CAAC,CACxD,EAAG,CAACT,CAAM,CAAC,EAELU,EAAevB,cAAY,IAAM,CACtC,GAAI,CAACa,EAAQ,OACb,MAAMS,EAAO,KAAK,UAAUT,EAAQ,KAAM,CAAC,EACrCW,EAAO,IAAI,KAAK,CAACF,CAAI,EAAG,CAAE,KAAM,mBAAoB,EACpDG,EAAM,IAAI,gBAAgBD,CAAI,EAC9BE,EAAI,SAAS,cAAc,GAAG,EACpCA,EAAE,KAAOD,EACTC,EAAE,SAAW,6BAA6Bb,EAAO,OAAO,QACxDa,EAAE,QACF,IAAI,gBAAgBD,CAAG,CACxB,EAAG,CAACZ,CAAM,CAAC,EAELc,EAAgBd,GAAQ,eAAe,QAAU,EAEvD,cACE5C,EAAA,CACA,UAAAN,OAACO,EAAA,CAAW,UAAU,kDACrB,UAAAN,MAACO,GAAU,wBAAY,EACtBwD,EAAgB,GAChBhE,OAAC,QAAK,UAAU,yBACd,UAAAgE,EAAc,kBAAgBA,IAAkB,EAAI,IAAM,GAAG,UAC/D,GAEF,SACCtD,EAAA,CACA,UAAAT,MAAC,KAAE,UAAU,qCAAqC,uKAIlD,EACAA,MAAC+C,EAAA,CAAO,KAAK,SAAS,QAAQ,QAAQ,QAASQ,EAAQ,SAAUpD,EAC/D,SAAAA,EAAU,aAAe,oBAC3B,EACCC,GAASJ,MAAC,OAAI,UAAU,4BAA6B,SAAAI,EAAM,EAC3D6C,GAAUI,GACVtD,OAAAW,WAAA,CACC,UAAAX,OAAC,OAAI,UAAU,uBACd,UAAAC,MAAC+C,EAAA,CAAO,KAAK,SAAS,QAAQ,UAAU,KAAK,KAAK,QAASU,EAAiB,6BAE5E,EACAzD,MAAC+C,EAAA,CAAO,KAAK,SAAS,QAAQ,UAAU,KAAK,KAAK,QAASY,EAAc,yBAEzE,EACA3D,MAAC+C,EAAA,CACA,KAAK,SACL,QAAQ,UACR,KAAK,KACL,QAAS,IAAMO,EAAaU,GAAM,CAACA,CAAC,EAEnC,WAAW,OAAS,QACtB,EACD,EACCD,EAAgB,GAChBhE,OAAC,OAAI,UAAU,OACd,UAAAA,OAAC,OAAI,UAAU,uCAAuC,8BACnCgE,EAAc,KACjC,EACA/D,MAAC,OAAI,UAAU,oGACb,cAAK,UAAUiD,EAAO,eAAgB,KAAM,CAAC,EAC/C,GACD,EAEDjD,MAAC,OAAI,UAAU,yGACb,cAAK,UAAUiD,EAAQ,KAAM,CAAC,EAChC,GACD,GAEF,GACD,CAEF,CClGO,SAASgB,GAA2C,CAC1D,KAAM,CAACC,EAAUC,CAAW,EAAIrC,WAAS,EAAK,EACxC,CAAC3B,EAASgD,CAAU,EAAIrB,WAAS,EAAI,EACrC,CAACsC,EAAQC,CAAS,EAAIvC,WAAS,EAAK,EACpC,CAAC1B,EAAOgD,CAAQ,EAAItB,WAAwB,IAAI,EAEhDwC,EAAOlC,cAAY,SAAY,CACpC,GAAI,CACH,KAAM,CAAE,oBAAAmC,CAAA,EAAwB,MAAAC,EAAA,oCAAAD,GAAA,KAAM,QAAO,sBAAkB,qDACzDE,EAAI,MAAMF,EAAA,EAChBJ,EAAYM,EAAE,QAAQ,CACvB,OAAS3B,EAAG,CACXM,EAASN,aAAa,MAAQA,EAAE,QAAU,OAAOA,CAAC,CAAC,CACpD,SACCK,EAAW,EAAK,CACjB,CACD,EAAG,EAAE,EAELuB,YAAU,IAAM,CAAOJ,EAAA,CAAQ,EAAG,CAACA,CAAI,CAAC,EAExC,MAAMK,EAASvC,cAAY,SAAY,CACtCiC,EAAU,EAAI,EACdjB,EAAS,IAAI,EACb,GAAI,CACH,KAAM,CAAE,kBAAAwB,CAAA,EAAsB,MAAAJ,EAAA,kCAAAI,GAAA,KAAM,QAAO,sBAAkB,mDACvDC,EAAO,CAACX,EACd,MAAMU,EAAkBC,CAAI,EAC5BV,EAAYU,CAAI,CACjB,OAAS/B,EAAG,CACXM,EAASN,aAAa,MAAQA,EAAE,QAAU,OAAOA,CAAC,CAAC,CACpD,SACCuB,EAAU,EAAK,CAChB,CACD,EAAG,CAACH,CAAQ,CAAC,EAEb,cACE7D,EAAA,CACA,UAAAL,MAACM,EAAA,CACA,SAAAN,MAACO,EAAA,CAAU,qCAAyB,EACrC,EACAP,MAACS,GACC,SAAAN,EACAH,MAAC,KAAE,UAAU,gCAAgC,oBAAQ,EAErDD,OAAAW,WAAA,CACC,UAAAX,OAAC,OAAI,UAAU,0BACd,UAAAC,MAAC8E,EAAA,CACA,QAASZ,EACT,gBAAiBS,EACjB,SAAUP,EACV,aAAW,mCAEZrE,OAAC,QAAK,UAAU,UAAU,6BACRmE,EAAW,KAAO,OACpC,GACD,EACAnE,OAAC,OAAI,UAAU,yBACd,UAAAA,OAAC,KAAE,kBACGC,MAAC,UAAO,cAAE,EAAS,0EAEzB,EACAD,OAAC,MAAG,UAAU,2BACb,UAAAA,OAAC,MACA,UAAAC,MAAC,UAAO,6BAAiB,EAAS,2NAKnC,SACC,MACA,UAAAA,MAAC,UAAO,wBAAY,EAAS,iKAI9B,GACD,EACAA,MAAC,KAAE,UAAU,0EAA0E,4QAMvF,EACAD,OAAC,KAAE,UAAU,wBAAwB,uCACT,IAC3BC,MAAC,QAAK,iDAAqC,EAAO,sEAEnD,EACCI,GAASL,OAAC,KAAE,UAAU,uBAAuB,oBAAQK,CAAA,EAAM,GAC7D,GACD,EAEF,GACD,CAEF,CCxFA,SAAwB2E,GAAqC,CAC5D,KAAM,CACL,KAAMC,EACN,QAASC,EACT,MAAOC,CAAA,EACJC,EACH/C,cAAY,IAAMgD,EAAA,EAAgB,EAAE,EACpC,CAAE,aAAc,KAAM,EAGjB,CACL,KAAMC,EACN,QAASC,EACT,MAAOC,CAAA,EACJJ,EACH/C,cAAY,IAAMoD,EAAA,EAAqB,EAAE,EACzC,CAAE,aAAc,IAAM,EAGvB,OACCzF,OAAC,OAAI,UAAU,sBACd,UAAAA,OAAC,OAAI,UAAU,uDACd,UAAAC,MAACC,EAAA,CACA,KAAMoF,EACN,QAASC,EACT,MAAOC,CAAA,SAEP9D,EAAA,CAAY,KAAMuD,EAAS,QAASC,EAAc,MAAOC,CAAA,CAAY,GACvE,QACCvD,EAAA,EAAY,QACZqB,EAAA,EAAgB,QAChBiB,EAAA,EAAqB,GACvB,CAEF","names":["ColumnAuditCard","columns","missing","c","jsxs","jsx","SchemaHealthCard","data","loading","error","Card","CardHeader","CardTitle","Badge","CardContent","Fragment","line","i","fmtBytes","b","FileSizesCard","files","pageSize","pageCount","freelistPages","total","TableRowCountsCard","tables","totalRows","t","DbStatsCard","ACTIONS","ActionsCard","results","setResults","useState","running","setRunning","days","setDays","handleAction","useCallback","def","action","res","postMaintenanceAction","prev","err","isRunning","lastResult","r","e","Button","DebugBundleCard","bundle","setBundle","setLoading","setError","showJson","setShowJson","gather","fetchDebugBundle","copyToClipboard","text","downloadJson","blob","url","a","criticalCount","v","HealthMitigationCard","mitigate","setMitigate","saving","setSaving","load","fetchHealthSettings","__vitePreload","s","useEffect","toggle","setHealthMitigate","next","Switch","MaintenanceTab","dbStats","statsLoading","statsError","useApi","fetchDbStats","schemaHealth","healthLoading","healthError","fetchSchemaHealth"],"ignoreList":[],"sources":["../../src/tabs/MaintenanceTab/SchemaHealthCard.tsx","../../src/tabs/MaintenanceTab/DbStatsCard.tsx","../../src/tabs/MaintenanceTab/ActionsCard.tsx","../../src/tabs/MaintenanceTab/DebugBundleCard.tsx","../../src/tabs/MaintenanceTab/HealthMitigationCard.tsx","../../src/tabs/MaintenanceTab.tsx"],"sourcesContent":["/**\n * dashboard-client/src/tabs/MaintenanceTab/SchemaHealthCard.tsx — Schema Health card.\n *\n * Extracted from MaintenanceTab.tsx (delegate-shell split). Renders schema\n * version, integrity check, column audit, and FK check from\n * GET /api/maintenance/schema-health.\n */\nimport type React from \"react\";\nimport type { SchemaHealthResponse } from \"@contracts\";\nimport { Badge } from \"../../components/ui/badge\";\nimport {\n\tCard,\n\tCardHeader,\n\tCardTitle,\n\tCardContent,\n} from \"../../components/ui/card\";\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=\"mt-3\">\n\t\t\t<div className=\"text-xs font-semibold text-muted-foreground\">\n\t\t\t\tColumn Audit ({columns.length} columns, {missing.length} missing)\n\t\t\t</div>\n\t\t\t<table className=\"mt-1 w-full border-collapse text-sm\">\n\t\t\t\t<thead>\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<th className=\"border-b border-border px-2 py-1 text-left font-medium text-muted-foreground\">\n\t\t\t\t\t\t\tTable\n\t\t\t\t\t\t</th>\n\t\t\t\t\t\t<th className=\"border-b border-border px-2 py-1 text-left font-medium text-muted-foreground\">\n\t\t\t\t\t\t\tColumn\n\t\t\t\t\t\t</th>\n\t\t\t\t\t\t<th className=\"border-b border-border px-2 py-1 text-left font-medium text-muted-foreground\">\n\t\t\t\t\t\t\tDecl\n\t\t\t\t\t\t</th>\n\t\t\t\t\t\t<th className=\"border-b border-border px-2 py-1 text-left font-medium text-muted-foreground\">\n\t\t\t\t\t\t\tStatus\n\t\t\t\t\t\t</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}`} className=\"border-b border-border/50\">\n\t\t\t\t\t\t\t<td className=\"px-2 py-1\">{c.table}</td>\n\t\t\t\t\t\t\t<td className=\"px-2 py-1\">{c.column}</td>\n\t\t\t\t\t\t\t<td className=\"px-2 py-1 font-mono\">{c.expectedDecl}</td>\n\t\t\t\t\t\t\t<td className={`px-2 py-1 ${c.present ? \"text-emerald-400\" : \"text-red-400\"}`}>\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\nexport function 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<Card>\n\t\t\t<CardHeader className=\"flex-row items-center justify-between space-y-0\">\n\t\t\t\t<CardTitle>Schema Health</CardTitle>\n\t\t\t\t{data && (\n\t\t\t\t\t<Badge variant={data.healthy ? \"success\" : \"danger\"}>\n\t\t\t\t\t\t{data.healthy ? \"HEALTHY\" : \"DEGRADED\"}\n\t\t\t\t\t</Badge>\n\t\t\t\t)}\n\t\t\t</CardHeader>\n\t\t\t<CardContent>\n\t\t\t\t{loading && !data && (\n\t\t\t\t\t<span className=\"text-sm text-muted-foreground\">Loading…</span>\n\t\t\t\t)}\n\t\t\t\t{error && !data && (\n\t\t\t\t\t<span className=\"text-sm text-red-400\">{error.message}</span>\n\t\t\t\t)}\n\t\t\t\t{data && (\n\t\t\t\t\t<>\n\t\t\t\t\t\t<div className=\"flex items-baseline justify-between border-b border-border py-1 text-sm\">\n\t\t\t\t\t\t\t<span className=\"text-muted-foreground\">Schema version</span>\n\t\t\t\t\t\t\t<span className=\"font-semibold\">{data.schemaVersion}</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div className=\"flex items-baseline justify-between border-b border-border py-1 text-sm\">\n\t\t\t\t\t\t\t<span className=\"text-muted-foreground\">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\"\n\t\t\t\t\t\t\t\t\t\t? \"text-emerald-400\"\n\t\t\t\t\t\t\t\t\t\t: \"text-red-400\"\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=\"mt-3\">\n\t\t\t\t\t\t\t\t<div className=\"text-xs font-semibold text-amber-400\">\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=\"font-mono text-xs text-amber-400\">\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</CardContent>\n\t\t</Card>\n\t);\n}\n","/**\n * dashboard-client/src/tabs/MaintenanceTab/DbStatsCard.tsx — DB Stats card.\n *\n * Extracted from MaintenanceTab.tsx (delegate-shell split). Renders file sizes\n * and table row counts from GET /api/maintenance.\n */\nimport type React from \"react\";\nimport type { DbStatsResponse } from \"@contracts\";\nimport {\n\tCard,\n\tCardHeader,\n\tCardTitle,\n\tCardContent,\n} from \"../../components/ui/card\";\n\n// ---------------------------------------------------------------------------\n// Format helpers (local — matches html.ts fmtBytes)\n// ---------------------------------------------------------------------------\n\nexport function 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=\"mt-3\">\n\t\t\t<div className=\"text-xs font-semibold text-muted-foreground\">File Sizes</div>\n\t\t\t<table className=\"mt-1 w-full border-collapse text-sm\">\n\t\t\t\t<tbody>\n\t\t\t\t\t<tr className=\"border-b border-border/50\">\n\t\t\t\t\t\t<td className=\"px-2 py-1\">sqlite.db</td>\n\t\t\t\t\t\t<td className=\"px-2 py-1 text-right font-semibold\">\n\t\t\t\t\t\t\t{fmtBytes(files.dbBytes)}\n\t\t\t\t\t\t</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 className=\"border-b border-border/50\">\n\t\t\t\t\t\t\t<td className=\"px-2 py-1\">.wal</td>\n\t\t\t\t\t\t\t<td className=\"px-2 py-1 text-right font-semibold\">\n\t\t\t\t\t\t\t\t{fmtBytes(files.walBytes)}\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\t{files.shmBytes > 0 && (\n\t\t\t\t\t\t<tr className=\"border-b border-border/50\">\n\t\t\t\t\t\t\t<td className=\"px-2 py-1\">.shm</td>\n\t\t\t\t\t\t\t<td className=\"px-2 py-1 text-right font-semibold\">\n\t\t\t\t\t\t\t\t{fmtBytes(files.shmBytes)}\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\t<tr className=\"border-b border-border/50\">\n\t\t\t\t\t\t<td className=\"px-2 py-1\">Total</td>\n\t\t\t\t\t\t<td className=\"px-2 py-1 text-right font-semibold\">\n\t\t\t\t\t\t\t{fmtBytes(total)}\n\t\t\t\t\t\t</td>\n\t\t\t\t\t</tr>\n\t\t\t\t\t<tr className=\"border-b border-border/50\">\n\t\t\t\t\t\t<td className=\"px-2 py-1\">Page size</td>\n\t\t\t\t\t\t<td className=\"px-2 py-1 text-right font-semibold\">\n\t\t\t\t\t\t\t{fmtBytes(pageSize)}\n\t\t\t\t\t\t</td>\n\t\t\t\t\t</tr>\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<td className=\"px-2 py-1\">Pages</td>\n\t\t\t\t\t\t<td className=\"px-2 py-1 text-right font-semibold\">\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=\"mt-3\">\n\t\t\t<div className=\"text-xs font-semibold text-muted-foreground\">\n\t\t\t\tTable Row Counts ({totalRows.toLocaleString()} total)\n\t\t\t</div>\n\t\t\t<table className=\"mt-1 w-full border-collapse text-sm\">\n\t\t\t\t<thead>\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<th className=\"border-b border-border px-2 py-1 text-left font-medium text-muted-foreground\">\n\t\t\t\t\t\t\tTable\n\t\t\t\t\t\t</th>\n\t\t\t\t\t\t<th className=\"border-b border-border px-2 py-1 text-right font-medium text-muted-foreground\">\n\t\t\t\t\t\t\tRows\n\t\t\t\t\t\t</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} className=\"border-b border-border/50\">\n\t\t\t\t\t\t\t<td className=\"px-2 py-1\">{t.table}</td>\n\t\t\t\t\t\t\t<td className=\"px-2 py-1 text-right\">\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\nexport function 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<Card>\n\t\t\t<CardHeader>\n\t\t\t\t<CardTitle>DB Stats</CardTitle>\n\t\t\t</CardHeader>\n\t\t\t<CardContent>\n\t\t\t\t{loading && !data && (\n\t\t\t\t\t<span className=\"text-sm text-muted-foreground\">Loading…</span>\n\t\t\t\t)}\n\t\t\t\t{error && !data && (\n\t\t\t\t\t<span className=\"text-sm text-red-400\">{error.message}</span>\n\t\t\t\t)}\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</CardContent>\n\t\t</Card>\n\t);\n}\n","/**\n * dashboard-client/src/tabs/MaintenanceTab/ActionsCard.tsx — Actions card.\n *\n * Extracted from MaintenanceTab.tsx (delegate-shell split). One-time\n * maintenance operations (vacuum, checkpoint, reindex, prune, …) with a\n * results history.\n */\nimport type React from \"react\";\nimport { useState, useCallback } from \"react\";\nimport type {\n\tMaintenanceAction,\n\tMaintenanceActionResult,\n} from \"@contracts\";\nimport { postMaintenanceAction } from \"../../api/client\";\nimport { Badge } from \"../../components/ui/badge\";\nimport { Button } from \"../../components/ui/button\";\nimport { Card, CardHeader, CardTitle } from \"../../components/ui/card\";\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// Actions card\n// ---------------------------------------------------------------------------\n\nexport function 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<Card>\n\t\t\t<CardHeader>\n\t\t\t\t<CardTitle>Actions</CardTitle>\n\t\t\t</CardHeader>\n\t\t\t<div className=\"grid grid-cols-1 gap-3 lg:grid-cols-2\">\n\t\t\t\t{ACTIONS.map((def) => {\n\t\t\t\t\tconst isRunning = running === def.key;\n\t\t\t\t\tconst lastResult = results.find((r) => r.operation === def.key);\n\t\t\t\t\treturn (\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tkey={def.key}\n\t\t\t\t\t\t\tclassName=\"flex items-start justify-between gap-3 rounded-lg border border-border/60 p-3\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<div className=\"min-w-0\">\n\t\t\t\t\t\t\t\t<div className=\"flex flex-wrap items-center gap-2\">\n\t\t\t\t\t\t\t\t\t<span className=\"font-semibold\">{def.label}</span>\n\t\t\t\t\t\t\t\t\t{def.dangerous && <Badge variant=\"danger\">DESTRUCTIVE</Badge>}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t<div className=\"mt-1 text-xs text-muted-foreground\">{def.desc}</div>\n\t\t\t\t\t\t\t\t{lastResult && (\n\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\tclassName={`mt-1 text-xs ${lastResult.success ? \"text-emerald-400\" : \"text-red-400\"}`}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{lastResult.summary}\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\t<div className=\"flex shrink-0 items-center gap-2\">\n\t\t\t\t\t\t\t\t{def.daysOld && (\n\t\t\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\t\t\ttype=\"number\"\n\t\t\t\t\t\t\t\t\t\tmin={1}\n\t\t\t\t\t\t\t\t\t\tmax={365}\n\t\t\t\t\t\t\t\t\t\tvalue={days}\n\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\tclassName=\"w-16 rounded-md border border-border bg-bg-elevated/50 px-2 py-1 text-sm outline-none focus:border-primary\"\n\t\t\t\t\t\t\t\t\t\tdisabled={isRunning}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tvariant={def.dangerous ? \"ghost\" : \"default\"}\n\t\t\t\t\t\t\t\t\tclassName={\n\t\t\t\t\t\t\t\t\t\tdef.dangerous\n\t\t\t\t\t\t\t\t\t\t\t? \"border border-red-500/50 text-red-400 hover:bg-red-500/10 hover:text-red-300\"\n\t\t\t\t\t\t\t\t\t\t\t: undefined\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tdisabled={running !== null}\n\t\t\t\t\t\t\t\t\tonClick={() => {\n\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\thandleAction(def);\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{isRunning ? \"Running…\" : \"Run\"}\n\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t);\n\t\t\t\t})}\n\t\t\t</div>\n\t\t</Card>\n\t);\n}\n","/**\n * dashboard-client/src/tabs/MaintenanceTab/DebugBundleCard.tsx — Debug Bundle card.\n *\n * Extracted from MaintenanceTab.tsx (delegate-shell split). Gather a diagnostic\n * bundle (events, config, schema health, store stats) and let the user copy or\n * download it for bug reports.\n */\nimport type React from \"react\";\nimport { useState, useCallback } from \"react\";\nimport type { DebugBundleResponse } from \"@contracts\";\nimport { fetchDebugBundle } from \"../../api/client\";\nimport { Button } from \"../../components/ui/button\";\nimport {\n\tCard,\n\tCardHeader,\n\tCardTitle,\n\tCardContent,\n} from \"../../components/ui/card\";\n\n// ---------------------------------------------------------------------------\n// Debug bundle card — gather diagnostic info for bug reports\n// ---------------------------------------------------------------------------\n\nexport function DebugBundleCard(): React.ReactElement {\n\tconst [bundle, setBundle] = useState<DebugBundleResponse | null>(null);\n\tconst [loading, setLoading] = useState(false);\n\tconst [error, setError] = useState<string | null>(null);\n\tconst [showJson, setShowJson] = useState(false);\n\n\tconst gather = useCallback(async () => {\n\t\tsetLoading(true);\n\t\tsetError(null);\n\t\ttry {\n\t\t\tconst res = await fetchDebugBundle();\n\t\t\tsetBundle(res);\n\t\t\tsetShowJson(true);\n\t\t} catch (err) {\n\t\t\tsetError(err instanceof Error ? err.message : String(err));\n\t\t} finally {\n\t\t\tsetLoading(false);\n\t\t}\n\t}, []);\n\n\tconst copyToClipboard = useCallback(() => {\n\t\tif (!bundle) return;\n\t\tconst text = JSON.stringify(bundle, null, 2);\n\t\tvoid navigator.clipboard.writeText(text).catch(() => {});\n\t}, [bundle]);\n\n\tconst downloadJson = useCallback(() => {\n\t\tif (!bundle) return;\n\t\tconst text = JSON.stringify(bundle, null, 2);\n\t\tconst blob = new Blob([text], { type: \"application/json\" });\n\t\tconst url = URL.createObjectURL(blob);\n\t\tconst a = document.createElement(\"a\");\n\t\ta.href = url;\n\t\ta.download = `mega-compact-debug-bundle-${bundle.builtAt}.json`;\n\t\ta.click();\n\t\tURL.revokeObjectURL(url);\n\t}, [bundle]);\n\n\tconst criticalCount = bundle?.criticalEvents.length ?? 0;\n\n\treturn (\n\t\t<Card>\n\t\t\t<CardHeader className=\"flex-row items-center justify-between space-y-0\">\n\t\t\t\t<CardTitle>Debug Bundle</CardTitle>\n\t\t\t\t{criticalCount > 0 && (\n\t\t\t\t\t<span className=\"text-xs text-amber-400\">\n\t\t\t\t\t\t{criticalCount} critical event{criticalCount !== 1 ? \"s\" : \"\"} found\n\t\t\t\t\t</span>\n\t\t\t\t)}\n\t\t\t</CardHeader>\n\t\t\t<CardContent>\n\t\t\t\t<p className=\"mb-3 text-sm text-muted-foreground\">\n\t\t\t\t\tGather a diagnostic bundle (recent events, config flags, schema health,\n\t\t\t\t\tstore stats) to attach to a bug report. Critical/compaction events are\n\t\t\t\t\thighlighted.\n\t\t\t\t</p>\n\t\t\t\t<Button type=\"button\" variant=\"glass\" onClick={gather} disabled={loading}>\n\t\t\t\t\t{loading ? \"Gathering…\" : \"Gather Debug Logs\"}\n\t\t\t\t</Button>\n\t\t\t\t{error && <div className=\"mt-2 text-sm text-red-400\">{error}</div>}\n\t\t\t\t{bundle && showJson && (\n\t\t\t\t\t<>\n\t\t\t\t\t\t<div className=\"mb-2 mt-3 flex gap-2\">\n\t\t\t\t\t\t\t<Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={copyToClipboard}>\n\t\t\t\t\t\t\t\tCopy to clipboard\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t<Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={downloadJson}>\n\t\t\t\t\t\t\t\tDownload JSON\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\t\t\tonClick={() => setShowJson((v) => !v)}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{showJson ? \"Hide\" : \"Show\"}\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t{criticalCount > 0 && (\n\t\t\t\t\t\t\t<div className=\"mt-3\">\n\t\t\t\t\t\t\t\t<div className=\"text-xs font-semibold text-amber-400\">\n\t\t\t\t\t\t\t\t\tCritical events ({criticalCount})\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t<pre className=\"max-h-48 overflow-auto rounded-md border border-border/60 bg-bg-elevated/40 p-3 font-mono text-xs\">\n\t\t\t\t\t\t\t\t\t{JSON.stringify(bundle.criticalEvents, null, 2)}\n\t\t\t\t\t\t\t\t</pre>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t)}\n\t\t\t\t\t\t<pre className=\"mt-3 max-h-96 overflow-auto rounded-md border border-border/60 bg-bg-elevated/40 p-3 font-mono text-xs\">\n\t\t\t\t\t\t\t{JSON.stringify(bundle, null, 2)}\n\t\t\t\t\t\t</pre>\n\t\t\t\t\t</>\n\t\t\t\t)}\n\t\t\t</CardContent>\n\t\t</Card>\n\t);\n}\n","/**\n * dashboard-client/src/tabs/MaintenanceTab/HealthMitigationCard.tsx — Health\n * Mitigation card.\n *\n * Extracted from MaintenanceTab.tsx (delegate-shell split). Toggles auto-\n * mitigation for degraded context (forced compaction / prefix break).\n */\nimport type React from \"react\";\nimport { useState, useCallback, useEffect } from \"react\";\nimport { Switch } from \"../../components/ui/switch\";\nimport {\n\tCard,\n\tCardHeader,\n\tCardTitle,\n\tCardContent,\n} from \"../../components/ui/card\";\n\n// ---------------------------------------------------------------------------\n// Health Mitigation card — toggle auto-mitigation for degraded context\n// ---------------------------------------------------------------------------\n\nexport function HealthMitigationCard(): React.ReactElement {\n\tconst [mitigate, setMitigate] = useState(false);\n\tconst [loading, setLoading] = useState(true);\n\tconst [saving, setSaving] = useState(false);\n\tconst [error, setError] = useState<string | null>(null);\n\n\tconst load = useCallback(async () => {\n\t\ttry {\n\t\t\tconst { fetchHealthSettings } = await import(\"../../api/health\");\n\t\t\tconst s = await fetchHealthSettings();\n\t\t\tsetMitigate(s.mitigate);\n\t\t} catch (e) {\n\t\t\tsetError(e instanceof Error ? e.message : String(e));\n\t\t} finally {\n\t\t\tsetLoading(false);\n\t\t}\n\t}, []);\n\n\tuseEffect(() => { void load(); }, [load]);\n\n\tconst toggle = useCallback(async () => {\n\t\tsetSaving(true);\n\t\tsetError(null);\n\t\ttry {\n\t\t\tconst { setHealthMitigate } = await import(\"../../api/health\");\n\t\t\tconst next = !mitigate;\n\t\t\tawait setHealthMitigate(next);\n\t\t\tsetMitigate(next);\n\t\t} catch (e) {\n\t\t\tsetError(e instanceof Error ? e.message : String(e));\n\t\t} finally {\n\t\t\tsetSaving(false);\n\t\t}\n\t}, [mitigate]);\n\n\treturn (\n\t\t<Card>\n\t\t\t<CardHeader>\n\t\t\t\t<CardTitle>Context Health Mitigation</CardTitle>\n\t\t\t</CardHeader>\n\t\t\t<CardContent>\n\t\t\t\t{loading ? (\n\t\t\t\t\t<p className=\"text-sm text-muted-foreground\">Loading…</p>\n\t\t\t\t) : (\n\t\t\t\t\t<>\n\t\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t<Switch\n\t\t\t\t\t\t\t\tchecked={mitigate}\n\t\t\t\t\t\t\t\tonCheckedChange={toggle}\n\t\t\t\t\t\t\t\tdisabled={saving}\n\t\t\t\t\t\t\t\taria-label=\"Context health auto-mitigation\"\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<span className=\"text-sm\">\n\t\t\t\t\t\t\t\tAuto-mitigation {mitigate ? \"ON\" : \"OFF\"}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div className=\"mt-3 space-y-2 text-sm\">\n\t\t\t\t\t\t\t<p>\n\t\t\t\t\t\t\t\tWhen <strong>ON</strong>, the extension automatically intervenes\n\t\t\t\t\t\t\t\twhen context health degrades:\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t<ul className=\"list-disc space-y-1 pl-5\">\n\t\t\t\t\t\t\t\t<li>\n\t\t\t\t\t\t\t\t\t<strong>Forced compaction</strong> — when the composite health\n\t\t\t\t\t\t\t\t\tscore drops below 0.4 (severe drift or garbled output), a\n\t\t\t\t\t\t\t\t\tcompaction is triggered to flush the degraded context and\n\t\t\t\t\t\t\t\t\trebuild from clean checkpoints, even if the context window\n\t\t\t\t\t\t\t\t\tisn't full.\n\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t\t<li>\n\t\t\t\t\t\t\t\t\t<strong>Prefix break</strong> — when KV cache poison is\n\t\t\t\t\t\t\t\t\tdetected (score < 0.3), the cached prefix is invalidated to\n\t\t\t\t\t\t\t\t\tforce a cache miss on the next turn, bypassing the corrupted KV\n\t\t\t\t\t\t\t\t\tstate.\n\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t</ul>\n\t\t\t\t\t\t\t<p className=\"rounded-md border border-amber-500/30 bg-amber-500/5 p-3 text-amber-400\">\n\t\t\t\t\t\t\t\tWARNING: both interventions have costs — forced compaction discards\n\t\t\t\t\t\t\t\tlive context, and prefix breaks kill cache savings. Only enable\n\t\t\t\t\t\t\t\twhen you're seeing degraded output (garbled text,\n\t\t\t\t\t\t\t\thallucinations, loss of coherence) and the Health tab confirms low\n\t\t\t\t\t\t\t\tscores.\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t<p className=\"text-muted-foreground\">\n\t\t\t\t\t\t\t\tAlso settable via env var:{\" \"}\n\t\t\t\t\t\t\t\t<code>MEGACOMPACT_CONTEXT_HEALTH_MITIGATE=1</code>. The dashboard\n\t\t\t\t\t\t\t\ttoggle persists to the store and takes precedence.\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t{error && <p className=\"text-sm text-red-400\">Error: {error}</p>}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</>\n\t\t\t\t)}\n\t\t\t</CardContent>\n\t\t</Card>\n\t);\n}\n","/**\n * dashboard-client/src/tabs/MaintenanceTab.tsx — Maintenance tab (v0.11.2 S49B).\n *\n * Delegate-shell (extensions split): three cards (Schema Health, DB Stats,\n * Actions) plus the Debug Bundle and Health Mitigation cards. The heavy card\n * components live in ./MaintenanceTab/* to keep every file under the extensions\n * line limit. All cards fetch independently; failures in one do not block the\n * others.\n */\n\nimport type React from \"react\";\nimport { useCallback } from \"react\";\nimport type {\n\tDbStatsResponse,\n\tSchemaHealthResponse,\n} from \"@contracts\";\nimport { useApi } from \"../hooks/useApi\";\nimport { fetchDbStats, fetchSchemaHealth } from \"../api/client\";\nimport { SchemaHealthCard } from \"./MaintenanceTab/SchemaHealthCard\";\nimport { DbStatsCard } from \"./MaintenanceTab/DbStatsCard\";\nimport { ActionsCard } from \"./MaintenanceTab/ActionsCard\";\nimport { DebugBundleCard } from \"./MaintenanceTab/DebugBundleCard\";\nimport { HealthMitigationCard } from \"./MaintenanceTab/HealthMitigationCard\";\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=\"flex flex-col gap-4\">\n\t\t\t<div className=\"grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-3\">\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\t<DebugBundleCard />\n\t\t\t<HealthMitigationCard />\n\t\t</div>\n\t);\n}\n"],"file":"assets/MaintenanceTab-DgKBZmdD.js"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{j as e,r as u,Z as V,$ as z}from"./index-tPP7N38D.js";import{T as P}from"./toggle-CHyUS1MH.js";const S={checkpoint:"#6366f1",turn:"#58a6ff","turn-content":"#22c55e",memory:"#f59e0b"},U={checkpoint:"Checkpoint",turn:"Turn","turn-content":"Turn Content",memory:"Memory"};function Y(t,s){return`M 0,-${s} L ${t},0 L 0,${s} L -${t},0 Z`}function Q(t){return!t||t==="active"?1:.5}function Z(t,s,l,h,n,p,c){const x=p?S[h]??"#6366f1":"#d1d5db",i=n?"#ef4444":"none",m=Q(c);switch(h){case"checkpoint":return e.jsx("circle",{cx:t,cy:s,r:n?l+3:l,fill:x,opacity:m,stroke:i,strokeWidth:n?2:0});case"turn":return e.jsx("circle",{cx:t,cy:s,r:n?l+3:l,fill:"none",stroke:x,strokeWidth:2.5,opacity:m});case"turn-content":return e.jsxs("g",{children:[e.jsx("circle",{cx:t,cy:s,r:n?l+3:l,fill:"none",stroke:x,strokeWidth:2.5,opacity:m}),e.jsx("circle",{cx:t,cy:s,r:Math.max(3,l*.3),fill:x,opacity:m})]});case"memory":const d=l*.85,g=l*.85;return e.jsx("path",{d:Y(d,g),transform:`translate(${t}, ${s})`,fill:x,opacity:m,stroke:i,strokeWidth:n?2:0})}}const X=5e3,O=.005,D=.01,_=.9,J=.1,L=50;function K(t){const s=t.nodes.map((n,p)=>{const c=2*Math.PI*p/Math.max(t.nodes.length,1),x=200;return{...n,pos:{x:Math.cos(c)*x+300,y:Math.sin(c)*x+200},vx:0,vy:0,pinned:!1}}),l=new Map;s.forEach((n,p)=>l.set(n.id,p));const h=t.edges.map(n=>{const p=l.get(n.source),c=l.get(n.target);return p===void 0||c===void 0||p===c?null:{source:p,target:c,weight:n.weight,type:n.type}}).filter(n=>n!==null);return{nodes:s,edges:h}}function ee(t){const{nodes:s,edges:l}=t,h=s.length;if(h!==0)for(let n=0;n<h;n++){if(s[n].pinned)continue;let p=0,c=0;for(let i=0;i<h;i++){if(n===i)continue;const m=s[n].pos.x-s[i].pos.x,d=s[n].pos.y-s[i].pos.y,g=m*m+d*d||1,v=X/g,y=Math.sqrt(g);p+=m/y*v,c+=d/y*v}for(const i of l){let m;if(i.source===n)m=i.target;else if(i.target===n)m=i.source;else continue;const d=s[m].pos.x-s[n].pos.x,g=s[m].pos.y-s[n].pos.y;p+=d*O*i.weight,c+=g*O*i.weight}p-=s[n].pos.x*D,c-=s[n].pos.y*D,s[n].vx=(s[n].vx+p)*_,s[n].vy=(s[n].vy+c)*_;const x=Math.sqrt(s[n].vx*s[n].vx+s[n].vy*s[n].vy);x>L&&(s[n].vx=s[n].vx/x*L,s[n].vy=s[n].vy/x*L),x>J&&(s[n].pos.x+=s[n].vx,s[n].pos.y+=s[n].vy)}}const te=300,re=.3,k=900,w=600,f=40;function M(t){switch(t){case"temporal":return"#6366f1";case"semantic":return"#22c55e";case"raptor_parent":return"#f59e0b"}}const N=({color:t,shape:s,label:l})=>{const h={display:"inline-block",width:"10px",height:"10px",verticalAlign:"middle",marginRight:"3px",borderRadius:s.startsWith("hollow")||s==="filled-circle"?"50%":void 0};let n;return s==="line"?e.jsx("span",{style:{color:t,marginRight:"6px"},children:l}):(s==="filled-circle"?n=e.jsx("span",{style:{...h,backgroundColor:t}}):s==="hollow-circle"?n=e.jsx("span",{style:{...h,border:`2px solid ${t}`,backgroundColor:"transparent"}}):s==="hollow-ring"?n=e.jsx("span",{style:{...h,border:`2px solid ${t}`,backgroundColor:"transparent",position:"relative"},children:e.jsx("span",{style:{display:"block",width:"4px",height:"4px",borderRadius:"50%",backgroundColor:t,margin:"2px auto"}})}):n=e.jsx("span",{style:{...h,clipPath:"polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%)",backgroundColor:t}}),e.jsxs(e.Fragment,{children:[e.jsxs("span",{className:"memory-map-legend-item",children:[n,l]})," "]}))},se=()=>{const[t,s]=u.useState(null),[l,h]=u.useState(!0),[n,p]=u.useState(null),[c,x]=u.useState(null),[i,m]=u.useState(""),[d]=u.useState(""),[g,v]=u.useState(0),y=u.useRef(null),T=u.useRef(0);u.useEffect(()=>{let r=!1;const o=new URLSearchParams;return d&&o.set("sessionId",d),o.set("threshold","0.7"),o.set("maxEdgesPerNode","4"),h(!0),p(null),fetch(`/api/memory-map?${o.toString()}`).then(a=>{if(!a.ok)throw new Error(`HTTP ${a.status}`);return a.json()}).then(a=>{r||(s(a),y.current=K(a),v(1),h(!1))}).catch(a=>{r||(p(a instanceof Error?a.message:"Unknown error"),h(!1))}),()=>{r=!0,cancelAnimationFrame(T.current)}},[d]),u.useEffect(()=>{if(!y.current||g===0)return;const r=y.current;let o=0;const a=()=>{o>=te||(ee(r),o++,v(o),T.current=requestAnimationFrame(a))};return T.current=requestAnimationFrame(a),()=>cancelAnimationFrame(T.current)},[g]);const F=u.useCallback((r,o)=>{const a=y.current;a&&(a.nodes[o].pinned=!a.nodes[o].pinned,a.nodes[o].pinned||(a.nodes[o].vx=0,a.nodes[o].vy=0),x(b=>b?.id===r.id?null:r),v(b=>b+1))},[]),A=u.useMemo(()=>{const r=y.current;if(!r||!i)return null;const o=i.toLowerCase();return new Set(r.nodes.map((a,b)=>a.summaryTruncated.toLowerCase().includes(o)||a.label.toLowerCase().includes(o)?b:-1).filter(a=>a>=0))},[i]),C=u.useMemo(()=>{const r=t?.validation;return r?r.gatesPassed<r.gatesRun?{level:"red",label:"Gate failure"}:r.warnings.length>0?{level:"yellow",label:`${r.warnings.length} warning(s)`}:{level:"green",label:"Healthy"}:{level:"yellow",label:"No validation"}},[t?.validation]),W={green:"#22c55e",yellow:"#eab308",red:"#ef4444"},H=u.useMemo(()=>{const r=t?.validation;if(!r)return`${t?.metadata.totalNodes??0} nodes`;const o=[];return r.sources.checkpoint>0&&o.push(`${r.sources.checkpoint} checkpoints`),r.sources.turn>0&&o.push(`${r.sources.turn} turns`),r.sources.turnContent>0&&o.push(`${r.sources.turnContent} turn-content`),r.sources.memory>0&&o.push(`${r.sources.memory} memories`),o.length===0&&o.push(`${t?.metadata.totalNodes??0} nodes`),o.join(" · ")},[t?.validation,t?.metadata.totalNodes]),I=u.useMemo(()=>{const r=t?.validation;if(!r)return"";const o=[];return o.push(r.sources.checkpoint>0?"✓ checkpoints":"✗ checkpoints"),o.push(r.sources.turn>0?"✓ turns":"✗ turns"),o.push(r.sources.turnContent>0?"✓ turn-content":"✗ turn-content"),o.push(r.sources.memory>0?"✓ memories":"✗ memories"),o.join(" | ")},[t?.validation]),B=u.useMemo(()=>{const r=t?.validation;return r?r.sources.checkpoint===0&&r.sources.turn===0&&r.sources.turnContent===0&&r.sources.memory===0:t?.metadata.totalNodes===0},[t?.validation,t?.metadata.totalNodes]);if(l)return e.jsx("div",{className:"rounded-lg border border-border bg-bg-card p-6 text-center text-sm text-muted-foreground",children:"Loading memory graph..."});if(n)return e.jsxs("div",{className:"rounded-lg border border-border bg-bg-card p-6 text-center text-sm",children:[e.jsxs("p",{className:"text-red-400",children:["Failed to load memory map: ",n]}),e.jsx("button",{className:"mt-3 rounded-md border border-border px-3 py-1.5 text-sm transition-colors hover:bg-bg-elevated hover:text-foreground",onClick:()=>v(r=>r+1),children:"Retry"})]});if(!t||!y.current||B)return e.jsx("div",{className:"rounded-lg border border-border bg-bg-card p-6 text-sm text-muted-foreground",children:e.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 j=y.current;return e.jsxs("div",{className:"flex flex-col gap-3",children:[e.jsxs("div",{className:"flex flex-wrap items-center gap-3",children:[e.jsx("input",{type:"text",placeholder:"Search memories...",value:i,onInput:r=>m(r.target.value),className:"w-56 rounded-md border border-border bg-bg-elevated/50 px-3 py-2 text-sm outline-none transition-colors focus:border-primary"}),e.jsxs("span",{className:"text-xs text-muted-foreground",children:[e.jsx("span",{children:H})," · ",e.jsxs("span",{children:[t.edges.length," edges"]})]}),e.jsx("span",{className:"ml-auto rounded-full px-2 py-1 text-[11px] font-semibold text-white",style:{backgroundColor:W[C.level]??"#6b7280"},title:`Validation: ${C.label}`,children:C.level==="green"?"Healthy":C.level==="yellow"?"Warnings":"Critical"})]}),I?e.jsx("div",{className:"text-[11px] text-muted-foreground",children:I}):null,e.jsxs("div",{className:"flex flex-wrap items-center gap-x-4 gap-y-1 rounded-lg border border-border/60 bg-bg-card px-3 py-2 text-xs",children:[e.jsx("span",{className:"font-semibold text-muted-foreground",children:"Nodes:"}),e.jsx(N,{type:"node",color:S.checkpoint,shape:"filled-circle",label:"Checkpoint"}),e.jsx(N,{type:"node",color:S.turn,shape:"hollow-circle",label:"Turn"}),e.jsx(N,{type:"node",color:S["turn-content"],shape:"hollow-ring",label:"Turn Content"}),e.jsx(N,{type:"node",color:S.memory,shape:"diamond",label:"Memory"}),e.jsx("span",{className:"font-semibold text-muted-foreground",children:"Edges:"}),e.jsx(N,{type:"edge",color:M("semantic"),shape:"line",label:"Semantic"}),e.jsx(N,{type:"edge",color:M("temporal"),shape:"line",label:"Temporal"}),e.jsx(N,{type:"edge",color:M("raptor_parent"),shape:"line",label:"Raptor"})]}),e.jsxs("svg",{width:k,height:w,viewBox:`0 0 ${k} ${w}`,className:"memory-map-svg",children:[j.edges.map((r,o)=>{const a=j.nodes[r.source],b=j.nodes[r.target],R=M(r.type),E=Math.max(1,r.weight*2.5);return e.jsx("line",{x1:f+a.pos.x/(j.nodes.length||1)*(k-2*f),y1:f+a.pos.y/(j.nodes.length||1)*(w-2*f),x2:f+b.pos.x/(j.nodes.length||1)*(k-2*f),y2:f+b.pos.y/(j.nodes.length||1)*(w-2*f),stroke:R,strokeWidth:E,opacity:re},`edge-${o}`)}),j.nodes.map((r,o)=>{const a=f+r.pos.x/(j.nodes.length||1)*(k-2*f),b=f+r.pos.y/(j.nodes.length||1)*(w-2*f),R=Math.min(20,8+r.tokenEstimate/500),E=A?A.has(o):!0,G=c?.id===r.id,q=r.label.length>20?r.label.slice(0,18)+"...":r.label;return e.jsxs("g",{className:"memory-map-node-group",onClick:()=>F(r,o),style:{cursor:"pointer"},children:[Z(a,b,R,r.nodeType,G,E,r.dedupStatus),e.jsx("text",{x:a,y:b+R+12,textAnchor:"middle",fontSize:"9px",fill:"#374151",className:"memory-map-label",children:q})]},`node-${o}`)})]}),c?e.jsxs("div",{className:"rounded-lg border border-border/60 bg-bg-card p-4 text-sm",children:[e.jsxs("h3",{className:"font-heading text-base font-semibold",children:[e.jsx("span",{className:"mr-1.5 text-xs font-normal text-muted-foreground",children:U[c.nodeType]}),c.label]}),e.jsx("p",{className:"mt-1",children:c.summaryTruncated}),c.topicSummary?e.jsxs("p",{className:"mt-1",children:["Topic: ",c.topicSummary]}):null,e.jsxs("p",{className:"mt-1",children:["Tokens: ",c.tokenEstimate," | Decisions: ",c.decisionCount]}),e.jsxs("p",{children:["Session: ",c.sessionId]}),e.jsxs("p",{children:["Raptor Level: ",c.raptorLevel]}),e.jsx("p",{className:"mt-1 text-muted-foreground",children:c.textSnippet})]}):null]})};function ne(t){return t===0?"#d4a017":t===1?"#3b82f6":"#14b8a6"}function $(t){return t?new Date(t).toLocaleString():"—"}function oe({node:t}){const s=ne(t.level),l=t.children.length>0?`${t.children.length} child${t.children.length===1?"":"ren"}`:"leaf";return e.jsxs("div",{style:{background:"#14142b",border:`1px solid ${s}`,borderRadius:"8px",padding:"0.75rem 1rem",marginBottom:"0.6rem",marginLeft:t.level*24,maxWidth:"90%"},children:[e.jsxs("div",{style:{display:"flex",gap:"0.75rem",alignItems:"center",marginBottom:"0.35rem"},children:[e.jsxs("span",{style:{display:"inline-block",background:s,color:"#fff",borderRadius:"10px",padding:"1px 8px",fontSize:"11px",fontWeight:600},children:["L",t.level]}),e.jsxs("span",{style:{fontSize:"12px",color:"#6b7280"},children:[l," · ",t.tokenEstimate," tok · ",t.qualityMarker]}),e.jsx("span",{style:{fontSize:"12px",color:"#6b7280",marginLeft:"auto"},children:$(t.builtAt)})]}),e.jsx("div",{style:{fontSize:"0.92rem",color:"#e5e7eb"},children:t.summary})]})}function le(){const[t,s]=u.useState(null),[l,h]=u.useState(null),[n,p]=u.useState(!0),[c,x]=u.useState(null);if(u.useEffect(()=>{let d=!1;return p(!0),x(null),Promise.all([V(),z()]).then(([g,v])=>{d||(s(g),h(v),p(!1))}).catch(g=>{d||(x(g instanceof Error?g.message:"Unknown error"),p(!1))}),()=>{d=!0}},[]),n)return e.jsx("div",{className:"rounded-lg border border-border bg-bg-card p-6 text-center text-sm text-muted-foreground",children:"Loading RAPTOR tree..."});if(c)return e.jsx("div",{className:"rounded-lg border border-dashed border-border bg-bg-card p-8 text-center text-sm",children:e.jsxs("p",{className:"text-red-400",children:["Failed to load RAPTOR tree: ",c]})});if(!t||t.empty||t.nodes.length===0)return e.jsx("div",{className:"rounded-lg border border-dashed border-border bg-bg-card p-8 text-center text-sm text-muted-foreground",children:e.jsx("p",{children:"No RAPTOR tree built yet — run a compaction (the tree builds during compaction)."})});const i=l&&!l.empty?l.builds[0]:null,m=i?.coherenceScore!=null?Math.round(i.coherenceScore*100):null;return e.jsxs("div",{className:"w-full p-4",children:[e.jsxs("div",{className:"mb-4 flex items-center gap-6 text-sm text-muted-foreground",children:[e.jsxs("span",{children:[e.jsx("strong",{children:t.nodes.length})," nodes"]}),e.jsxs("span",{children:[e.jsx("strong",{children:t.levels+1})," levels"]}),e.jsxs("span",{children:["Built ",$(t.builtAt??0)]}),i&&e.jsxs(e.Fragment,{children:[e.jsxs("span",{children:["Coherence:"," ",e.jsx("strong",{style:{color:m!=null&&m>=70?"#22c55e":"#f59e0b"},children:m!=null?`${m}%`:"—"})]}),e.jsxs("span",{children:["Leaves: ",e.jsx("strong",{children:i.leafCount})]}),e.jsxs("span",{children:["Depth: ",e.jsx("strong",{children:i.depth})]}),i.timedOut&&e.jsx("span",{style:{color:"#ef4444"},children:"budget timeout"})]})]}),l&&!l.empty&&l.builds.length>1&&e.jsxs("details",{className:"mb-4",children:[e.jsxs("summary",{className:"cursor-pointer text-xs text-muted-foreground",children:["Build history (",l.builds.length," builds)"]}),e.jsx("div",{className:"mt-2 space-y-1",children:l.builds.slice(0,10).map(d=>e.jsxs("div",{className:"flex items-center gap-4 text-xs text-muted-foreground",children:[e.jsx("span",{children:$(d.completedAt)}),e.jsxs("span",{children:[d.nodeCount," nodes"]}),e.jsxs("span",{children:[d.leafCount," leaves"]}),e.jsxs("span",{children:["depth ",d.depth]}),e.jsxs("span",{children:["coherence"," ",d.coherenceScore!=null?`${Math.round(d.coherenceScore*100)}%`:"—"]}),d.timedOut&&e.jsx("span",{style:{color:"#ef4444"},children:"timeout"})]},d.buildId))})]}),t.nodes.map(d=>e.jsx(oe,{node:d},d.id))]})}const ie=()=>{const[t,s]=u.useState("map");return e.jsxs("div",{className:"flex flex-col gap-4",children:[e.jsxs("nav",{className:"flex gap-2 border-b border-border pb-2 pt-2","aria-label":"Memory map sections",children:[e.jsx(P,{pressed:t==="map",onClick:()=>s("map"),"aria-label":"Memory Map view",children:"Memory Map"}),e.jsx(P,{pressed:t==="raptor",onClick:()=>s("raptor"),"aria-label":"RAPTOR Tree view",children:"RAPTOR Tree"})]}),t==="map"&&e.jsx(se,{}),t==="raptor"&&e.jsx(le,{})]})};export{ie as default};
|
|
2
|
-
//# sourceMappingURL=MemoryMapTab-
|
|
1
|
+
import{j as e,r as u,Z as V,$ as z}from"./index-CRHmWnRi.js";import{T as P}from"./toggle-DT1AN5wS.js";const S={checkpoint:"#6366f1",turn:"#58a6ff","turn-content":"#22c55e",memory:"#f59e0b"},U={checkpoint:"Checkpoint",turn:"Turn","turn-content":"Turn Content",memory:"Memory"};function Y(t,s){return`M 0,-${s} L ${t},0 L 0,${s} L -${t},0 Z`}function Q(t){return!t||t==="active"?1:.5}function Z(t,s,l,h,n,p,c){const x=p?S[h]??"#6366f1":"#d1d5db",i=n?"#ef4444":"none",m=Q(c);switch(h){case"checkpoint":return e.jsx("circle",{cx:t,cy:s,r:n?l+3:l,fill:x,opacity:m,stroke:i,strokeWidth:n?2:0});case"turn":return e.jsx("circle",{cx:t,cy:s,r:n?l+3:l,fill:"none",stroke:x,strokeWidth:2.5,opacity:m});case"turn-content":return e.jsxs("g",{children:[e.jsx("circle",{cx:t,cy:s,r:n?l+3:l,fill:"none",stroke:x,strokeWidth:2.5,opacity:m}),e.jsx("circle",{cx:t,cy:s,r:Math.max(3,l*.3),fill:x,opacity:m})]});case"memory":const d=l*.85,g=l*.85;return e.jsx("path",{d:Y(d,g),transform:`translate(${t}, ${s})`,fill:x,opacity:m,stroke:i,strokeWidth:n?2:0})}}const X=5e3,O=.005,D=.01,_=.9,J=.1,L=50;function K(t){const s=t.nodes.map((n,p)=>{const c=2*Math.PI*p/Math.max(t.nodes.length,1),x=200;return{...n,pos:{x:Math.cos(c)*x+300,y:Math.sin(c)*x+200},vx:0,vy:0,pinned:!1}}),l=new Map;s.forEach((n,p)=>l.set(n.id,p));const h=t.edges.map(n=>{const p=l.get(n.source),c=l.get(n.target);return p===void 0||c===void 0||p===c?null:{source:p,target:c,weight:n.weight,type:n.type}}).filter(n=>n!==null);return{nodes:s,edges:h}}function ee(t){const{nodes:s,edges:l}=t,h=s.length;if(h!==0)for(let n=0;n<h;n++){if(s[n].pinned)continue;let p=0,c=0;for(let i=0;i<h;i++){if(n===i)continue;const m=s[n].pos.x-s[i].pos.x,d=s[n].pos.y-s[i].pos.y,g=m*m+d*d||1,v=X/g,y=Math.sqrt(g);p+=m/y*v,c+=d/y*v}for(const i of l){let m;if(i.source===n)m=i.target;else if(i.target===n)m=i.source;else continue;const d=s[m].pos.x-s[n].pos.x,g=s[m].pos.y-s[n].pos.y;p+=d*O*i.weight,c+=g*O*i.weight}p-=s[n].pos.x*D,c-=s[n].pos.y*D,s[n].vx=(s[n].vx+p)*_,s[n].vy=(s[n].vy+c)*_;const x=Math.sqrt(s[n].vx*s[n].vx+s[n].vy*s[n].vy);x>L&&(s[n].vx=s[n].vx/x*L,s[n].vy=s[n].vy/x*L),x>J&&(s[n].pos.x+=s[n].vx,s[n].pos.y+=s[n].vy)}}const te=300,re=.3,k=900,w=600,f=40;function M(t){switch(t){case"temporal":return"#6366f1";case"semantic":return"#22c55e";case"raptor_parent":return"#f59e0b"}}const N=({color:t,shape:s,label:l})=>{const h={display:"inline-block",width:"10px",height:"10px",verticalAlign:"middle",marginRight:"3px",borderRadius:s.startsWith("hollow")||s==="filled-circle"?"50%":void 0};let n;return s==="line"?e.jsx("span",{style:{color:t,marginRight:"6px"},children:l}):(s==="filled-circle"?n=e.jsx("span",{style:{...h,backgroundColor:t}}):s==="hollow-circle"?n=e.jsx("span",{style:{...h,border:`2px solid ${t}`,backgroundColor:"transparent"}}):s==="hollow-ring"?n=e.jsx("span",{style:{...h,border:`2px solid ${t}`,backgroundColor:"transparent",position:"relative"},children:e.jsx("span",{style:{display:"block",width:"4px",height:"4px",borderRadius:"50%",backgroundColor:t,margin:"2px auto"}})}):n=e.jsx("span",{style:{...h,clipPath:"polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%)",backgroundColor:t}}),e.jsxs(e.Fragment,{children:[e.jsxs("span",{className:"memory-map-legend-item",children:[n,l]})," "]}))},se=()=>{const[t,s]=u.useState(null),[l,h]=u.useState(!0),[n,p]=u.useState(null),[c,x]=u.useState(null),[i,m]=u.useState(""),[d]=u.useState(""),[g,v]=u.useState(0),y=u.useRef(null),T=u.useRef(0);u.useEffect(()=>{let r=!1;const o=new URLSearchParams;return d&&o.set("sessionId",d),o.set("threshold","0.7"),o.set("maxEdgesPerNode","4"),h(!0),p(null),fetch(`/api/memory-map?${o.toString()}`).then(a=>{if(!a.ok)throw new Error(`HTTP ${a.status}`);return a.json()}).then(a=>{r||(s(a),y.current=K(a),v(1),h(!1))}).catch(a=>{r||(p(a instanceof Error?a.message:"Unknown error"),h(!1))}),()=>{r=!0,cancelAnimationFrame(T.current)}},[d]),u.useEffect(()=>{if(!y.current||g===0)return;const r=y.current;let o=0;const a=()=>{o>=te||(ee(r),o++,v(o),T.current=requestAnimationFrame(a))};return T.current=requestAnimationFrame(a),()=>cancelAnimationFrame(T.current)},[g]);const F=u.useCallback((r,o)=>{const a=y.current;a&&(a.nodes[o].pinned=!a.nodes[o].pinned,a.nodes[o].pinned||(a.nodes[o].vx=0,a.nodes[o].vy=0),x(b=>b?.id===r.id?null:r),v(b=>b+1))},[]),A=u.useMemo(()=>{const r=y.current;if(!r||!i)return null;const o=i.toLowerCase();return new Set(r.nodes.map((a,b)=>a.summaryTruncated.toLowerCase().includes(o)||a.label.toLowerCase().includes(o)?b:-1).filter(a=>a>=0))},[i]),C=u.useMemo(()=>{const r=t?.validation;return r?r.gatesPassed<r.gatesRun?{level:"red",label:"Gate failure"}:r.warnings.length>0?{level:"yellow",label:`${r.warnings.length} warning(s)`}:{level:"green",label:"Healthy"}:{level:"yellow",label:"No validation"}},[t?.validation]),W={green:"#22c55e",yellow:"#eab308",red:"#ef4444"},H=u.useMemo(()=>{const r=t?.validation;if(!r)return`${t?.metadata.totalNodes??0} nodes`;const o=[];return r.sources.checkpoint>0&&o.push(`${r.sources.checkpoint} checkpoints`),r.sources.turn>0&&o.push(`${r.sources.turn} turns`),r.sources.turnContent>0&&o.push(`${r.sources.turnContent} turn-content`),r.sources.memory>0&&o.push(`${r.sources.memory} memories`),o.length===0&&o.push(`${t?.metadata.totalNodes??0} nodes`),o.join(" · ")},[t?.validation,t?.metadata.totalNodes]),I=u.useMemo(()=>{const r=t?.validation;if(!r)return"";const o=[];return o.push(r.sources.checkpoint>0?"✓ checkpoints":"✗ checkpoints"),o.push(r.sources.turn>0?"✓ turns":"✗ turns"),o.push(r.sources.turnContent>0?"✓ turn-content":"✗ turn-content"),o.push(r.sources.memory>0?"✓ memories":"✗ memories"),o.join(" | ")},[t?.validation]),B=u.useMemo(()=>{const r=t?.validation;return r?r.sources.checkpoint===0&&r.sources.turn===0&&r.sources.turnContent===0&&r.sources.memory===0:t?.metadata.totalNodes===0},[t?.validation,t?.metadata.totalNodes]);if(l)return e.jsx("div",{className:"rounded-lg border border-border bg-bg-card p-6 text-center text-sm text-muted-foreground",children:"Loading memory graph..."});if(n)return e.jsxs("div",{className:"rounded-lg border border-border bg-bg-card p-6 text-center text-sm",children:[e.jsxs("p",{className:"text-red-400",children:["Failed to load memory map: ",n]}),e.jsx("button",{className:"mt-3 rounded-md border border-border px-3 py-1.5 text-sm transition-colors hover:bg-bg-elevated hover:text-foreground",onClick:()=>v(r=>r+1),children:"Retry"})]});if(!t||!y.current||B)return e.jsx("div",{className:"rounded-lg border border-border bg-bg-card p-6 text-sm text-muted-foreground",children:e.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 j=y.current;return e.jsxs("div",{className:"flex flex-col gap-3",children:[e.jsxs("div",{className:"flex flex-wrap items-center gap-3",children:[e.jsx("input",{type:"text",placeholder:"Search memories...",value:i,onInput:r=>m(r.target.value),className:"w-56 rounded-md border border-border bg-bg-elevated/50 px-3 py-2 text-sm outline-none transition-colors focus:border-primary"}),e.jsxs("span",{className:"text-xs text-muted-foreground",children:[e.jsx("span",{children:H})," · ",e.jsxs("span",{children:[t.edges.length," edges"]})]}),e.jsx("span",{className:"ml-auto rounded-full px-2 py-1 text-[11px] font-semibold text-white",style:{backgroundColor:W[C.level]??"#6b7280"},title:`Validation: ${C.label}`,children:C.level==="green"?"Healthy":C.level==="yellow"?"Warnings":"Critical"})]}),I?e.jsx("div",{className:"text-[11px] text-muted-foreground",children:I}):null,e.jsxs("div",{className:"flex flex-wrap items-center gap-x-4 gap-y-1 rounded-lg border border-border/60 bg-bg-card px-3 py-2 text-xs",children:[e.jsx("span",{className:"font-semibold text-muted-foreground",children:"Nodes:"}),e.jsx(N,{type:"node",color:S.checkpoint,shape:"filled-circle",label:"Checkpoint"}),e.jsx(N,{type:"node",color:S.turn,shape:"hollow-circle",label:"Turn"}),e.jsx(N,{type:"node",color:S["turn-content"],shape:"hollow-ring",label:"Turn Content"}),e.jsx(N,{type:"node",color:S.memory,shape:"diamond",label:"Memory"}),e.jsx("span",{className:"font-semibold text-muted-foreground",children:"Edges:"}),e.jsx(N,{type:"edge",color:M("semantic"),shape:"line",label:"Semantic"}),e.jsx(N,{type:"edge",color:M("temporal"),shape:"line",label:"Temporal"}),e.jsx(N,{type:"edge",color:M("raptor_parent"),shape:"line",label:"Raptor"})]}),e.jsxs("svg",{width:k,height:w,viewBox:`0 0 ${k} ${w}`,className:"memory-map-svg",children:[j.edges.map((r,o)=>{const a=j.nodes[r.source],b=j.nodes[r.target],R=M(r.type),E=Math.max(1,r.weight*2.5);return e.jsx("line",{x1:f+a.pos.x/(j.nodes.length||1)*(k-2*f),y1:f+a.pos.y/(j.nodes.length||1)*(w-2*f),x2:f+b.pos.x/(j.nodes.length||1)*(k-2*f),y2:f+b.pos.y/(j.nodes.length||1)*(w-2*f),stroke:R,strokeWidth:E,opacity:re},`edge-${o}`)}),j.nodes.map((r,o)=>{const a=f+r.pos.x/(j.nodes.length||1)*(k-2*f),b=f+r.pos.y/(j.nodes.length||1)*(w-2*f),R=Math.min(20,8+r.tokenEstimate/500),E=A?A.has(o):!0,G=c?.id===r.id,q=r.label.length>20?r.label.slice(0,18)+"...":r.label;return e.jsxs("g",{className:"memory-map-node-group",onClick:()=>F(r,o),style:{cursor:"pointer"},children:[Z(a,b,R,r.nodeType,G,E,r.dedupStatus),e.jsx("text",{x:a,y:b+R+12,textAnchor:"middle",fontSize:"9px",fill:"#374151",className:"memory-map-label",children:q})]},`node-${o}`)})]}),c?e.jsxs("div",{className:"rounded-lg border border-border/60 bg-bg-card p-4 text-sm",children:[e.jsxs("h3",{className:"font-heading text-base font-semibold",children:[e.jsx("span",{className:"mr-1.5 text-xs font-normal text-muted-foreground",children:U[c.nodeType]}),c.label]}),e.jsx("p",{className:"mt-1",children:c.summaryTruncated}),c.topicSummary?e.jsxs("p",{className:"mt-1",children:["Topic: ",c.topicSummary]}):null,e.jsxs("p",{className:"mt-1",children:["Tokens: ",c.tokenEstimate," | Decisions: ",c.decisionCount]}),e.jsxs("p",{children:["Session: ",c.sessionId]}),e.jsxs("p",{children:["Raptor Level: ",c.raptorLevel]}),e.jsx("p",{className:"mt-1 text-muted-foreground",children:c.textSnippet})]}):null]})};function ne(t){return t===0?"#d4a017":t===1?"#3b82f6":"#14b8a6"}function $(t){return t?new Date(t).toLocaleString():"—"}function oe({node:t}){const s=ne(t.level),l=t.children.length>0?`${t.children.length} child${t.children.length===1?"":"ren"}`:"leaf";return e.jsxs("div",{style:{background:"#14142b",border:`1px solid ${s}`,borderRadius:"8px",padding:"0.75rem 1rem",marginBottom:"0.6rem",marginLeft:t.level*24,maxWidth:"90%"},children:[e.jsxs("div",{style:{display:"flex",gap:"0.75rem",alignItems:"center",marginBottom:"0.35rem"},children:[e.jsxs("span",{style:{display:"inline-block",background:s,color:"#fff",borderRadius:"10px",padding:"1px 8px",fontSize:"11px",fontWeight:600},children:["L",t.level]}),e.jsxs("span",{style:{fontSize:"12px",color:"#6b7280"},children:[l," · ",t.tokenEstimate," tok · ",t.qualityMarker]}),e.jsx("span",{style:{fontSize:"12px",color:"#6b7280",marginLeft:"auto"},children:$(t.builtAt)})]}),e.jsx("div",{style:{fontSize:"0.92rem",color:"#e5e7eb"},children:t.summary})]})}function le(){const[t,s]=u.useState(null),[l,h]=u.useState(null),[n,p]=u.useState(!0),[c,x]=u.useState(null);if(u.useEffect(()=>{let d=!1;return p(!0),x(null),Promise.all([V(),z()]).then(([g,v])=>{d||(s(g),h(v),p(!1))}).catch(g=>{d||(x(g instanceof Error?g.message:"Unknown error"),p(!1))}),()=>{d=!0}},[]),n)return e.jsx("div",{className:"rounded-lg border border-border bg-bg-card p-6 text-center text-sm text-muted-foreground",children:"Loading RAPTOR tree..."});if(c)return e.jsx("div",{className:"rounded-lg border border-dashed border-border bg-bg-card p-8 text-center text-sm",children:e.jsxs("p",{className:"text-red-400",children:["Failed to load RAPTOR tree: ",c]})});if(!t||t.empty||t.nodes.length===0)return e.jsx("div",{className:"rounded-lg border border-dashed border-border bg-bg-card p-8 text-center text-sm text-muted-foreground",children:e.jsx("p",{children:"No RAPTOR tree built yet — run a compaction (the tree builds during compaction)."})});const i=l&&!l.empty?l.builds[0]:null,m=i?.coherenceScore!=null?Math.round(i.coherenceScore*100):null;return e.jsxs("div",{className:"w-full p-4",children:[e.jsxs("div",{className:"mb-4 flex items-center gap-6 text-sm text-muted-foreground",children:[e.jsxs("span",{children:[e.jsx("strong",{children:t.nodes.length})," nodes"]}),e.jsxs("span",{children:[e.jsx("strong",{children:t.levels+1})," levels"]}),e.jsxs("span",{children:["Built ",$(t.builtAt??0)]}),i&&e.jsxs(e.Fragment,{children:[e.jsxs("span",{children:["Coherence:"," ",e.jsx("strong",{style:{color:m!=null&&m>=70?"#22c55e":"#f59e0b"},children:m!=null?`${m}%`:"—"})]}),e.jsxs("span",{children:["Leaves: ",e.jsx("strong",{children:i.leafCount})]}),e.jsxs("span",{children:["Depth: ",e.jsx("strong",{children:i.depth})]}),i.timedOut&&e.jsx("span",{style:{color:"#ef4444"},children:"budget timeout"})]})]}),l&&!l.empty&&l.builds.length>1&&e.jsxs("details",{className:"mb-4",children:[e.jsxs("summary",{className:"cursor-pointer text-xs text-muted-foreground",children:["Build history (",l.builds.length," builds)"]}),e.jsx("div",{className:"mt-2 space-y-1",children:l.builds.slice(0,10).map(d=>e.jsxs("div",{className:"flex items-center gap-4 text-xs text-muted-foreground",children:[e.jsx("span",{children:$(d.completedAt)}),e.jsxs("span",{children:[d.nodeCount," nodes"]}),e.jsxs("span",{children:[d.leafCount," leaves"]}),e.jsxs("span",{children:["depth ",d.depth]}),e.jsxs("span",{children:["coherence"," ",d.coherenceScore!=null?`${Math.round(d.coherenceScore*100)}%`:"—"]}),d.timedOut&&e.jsx("span",{style:{color:"#ef4444"},children:"timeout"})]},d.buildId))})]}),t.nodes.map(d=>e.jsx(oe,{node:d},d.id))]})}const ie=()=>{const[t,s]=u.useState("map");return e.jsxs("div",{className:"flex flex-col gap-4",children:[e.jsxs("nav",{className:"flex gap-2 border-b border-border pb-2 pt-2","aria-label":"Memory map sections",children:[e.jsx(P,{pressed:t==="map",onClick:()=>s("map"),"aria-label":"Memory Map view",children:"Memory Map"}),e.jsx(P,{pressed:t==="raptor",onClick:()=>s("raptor"),"aria-label":"RAPTOR Tree view",children:"RAPTOR Tree"})]}),t==="map"&&e.jsx(se,{}),t==="raptor"&&e.jsx(le,{})]})};export{ie as default};
|
|
2
|
+
//# sourceMappingURL=MemoryMapTab-SwTOzuZW.js.map
|