pi-mega-compact 0.20.78 → 0.20.80

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.
Files changed (107) hide show
  1. package/dist/extensions/dashboard-server/routes-rag-settings-helpers.js +1 -0
  2. package/dist/extensions/mega-config.js +2 -0
  3. package/dist/extensions/mega-events/analytics-handler.js +169 -0
  4. package/dist/extensions/mega-events/register.js +3 -0
  5. package/dist/src/queryExpansion.js +1 -0
  6. package/dist/src/store/analytics/connection.js +114 -0
  7. package/dist/src/store/analytics/contract-compliance.test/_helpers.js +39 -0
  8. package/dist/src/store/analytics/index.js +25 -0
  9. package/dist/src/store/analytics/memory-store.js +111 -0
  10. package/dist/src/store/analytics/schema.js +108 -0
  11. package/dist/src/store/analytics/sqlite-store/admin.js +84 -0
  12. package/dist/src/store/analytics/sqlite-store/ctx.js +1 -0
  13. package/dist/src/store/analytics/sqlite-store/read.js +25 -0
  14. package/dist/src/store/analytics/sqlite-store/rows.js +44 -0
  15. package/dist/src/store/analytics/sqlite-store/write.js +90 -0
  16. package/dist/src/store/analytics/sqlite-store.js +79 -0
  17. package/dist/src/store/analytics/types.js +17 -0
  18. package/dist/src/store/sqlite/utils.js +7 -0
  19. package/dist/src/store/turns/connection.js +1 -1
  20. package/dist/src/vector-cortex/ledger/sqlite.js +1 -0
  21. package/dist/vector-cortex/ledger/sqlite.js +1 -0
  22. package/extensions/dashboard-client/dist/assets/{AdminTab-BsA8-_bX.js → AdminTab-C-kTQx7a.js} +2 -2
  23. package/extensions/dashboard-client/dist/assets/{AdminTab-BsA8-_bX.js.map → AdminTab-C-kTQx7a.js.map} +1 -1
  24. package/extensions/dashboard-client/dist/assets/{AreaChart-BRHEfWhc.js → AreaChart-BmntvH86.js} +2 -2
  25. package/extensions/dashboard-client/dist/assets/{AreaChart-BRHEfWhc.js.map → AreaChart-BmntvH86.js.map} +1 -1
  26. package/extensions/dashboard-client/dist/assets/{BarChart-C-M0LaFc.js → BarChart-D_AStVsb.js} +2 -2
  27. package/extensions/dashboard-client/dist/assets/{BarChart-C-M0LaFc.js.map → BarChart-D_AStVsb.js.map} +1 -1
  28. package/extensions/dashboard-client/dist/assets/{CacheTab-Cj3n59U3.js → CacheTab-VRFk7EPo.js} +2 -2
  29. package/extensions/dashboard-client/dist/assets/{CacheTab-Cj3n59U3.js.map → CacheTab-VRFk7EPo.js.map} +1 -1
  30. package/extensions/dashboard-client/dist/assets/{ConfigTab-C016XTNC.js → ConfigTab-Ci5LOohz.js} +2 -2
  31. package/extensions/dashboard-client/dist/assets/{ConfigTab-C016XTNC.js.map → ConfigTab-Ci5LOohz.js.map} +1 -1
  32. package/extensions/dashboard-client/dist/assets/{EventsTab-DE29fuqe.js → EventsTab-8Y7if14O.js} +2 -2
  33. package/extensions/dashboard-client/dist/assets/{EventsTab-DE29fuqe.js.map → EventsTab-8Y7if14O.js.map} +1 -1
  34. package/extensions/dashboard-client/dist/assets/{HealthTab-D4eVI8ar.js → HealthTab-Cw5eYIYq.js} +2 -2
  35. package/extensions/dashboard-client/dist/assets/{HealthTab-D4eVI8ar.js.map → HealthTab-Cw5eYIYq.js.map} +1 -1
  36. package/extensions/dashboard-client/dist/assets/{MaintenanceTab-4fJ7M4S4.js → MaintenanceTab-B0o3a57B.js} +2 -2
  37. package/extensions/dashboard-client/dist/assets/{MaintenanceTab-4fJ7M4S4.js.map → MaintenanceTab-B0o3a57B.js.map} +1 -1
  38. package/extensions/dashboard-client/dist/assets/{MemoryMapTab-CDbcK9e4.js → MemoryMapTab-JV8mLhsc.js} +2 -2
  39. package/extensions/dashboard-client/dist/assets/{MemoryMapTab-CDbcK9e4.js.map → MemoryMapTab-JV8mLhsc.js.map} +1 -1
  40. package/extensions/dashboard-client/dist/assets/{MetricsCards-VUEcgGzo.js → MetricsCards-Bkcv7Nl8.js} +2 -2
  41. package/extensions/dashboard-client/dist/assets/{MetricsCards-VUEcgGzo.js.map → MetricsCards-Bkcv7Nl8.js.map} +1 -1
  42. package/extensions/dashboard-client/dist/assets/MetricsTab-CAG4VgbM.js +2 -0
  43. package/extensions/dashboard-client/dist/assets/{MetricsTab-CKqJ0Zl-.js.map → MetricsTab-CAG4VgbM.js.map} +1 -1
  44. package/extensions/dashboard-client/dist/assets/OverviewTab-gxdUR58S.js +16 -0
  45. package/extensions/dashboard-client/dist/assets/OverviewTab-gxdUR58S.js.map +1 -0
  46. package/extensions/dashboard-client/dist/assets/{ReposTab-C4iveqrP.js → ReposTab-2hMRSS76.js} +2 -2
  47. package/extensions/dashboard-client/dist/assets/{ReposTab-C4iveqrP.js.map → ReposTab-2hMRSS76.js.map} +1 -1
  48. package/extensions/dashboard-client/dist/assets/{SessionsTab-DSEwkMQm.js → SessionsTab-Pjx5TBtW.js} +2 -2
  49. package/extensions/dashboard-client/dist/assets/{SessionsTab-DSEwkMQm.js.map → SessionsTab-Pjx5TBtW.js.map} +1 -1
  50. package/extensions/dashboard-client/dist/assets/{SetupTab-CCyWgy4O.js → SetupTab-B6Y3eHXo.js} +2 -2
  51. package/extensions/dashboard-client/dist/assets/{SetupTab-CCyWgy4O.js.map → SetupTab-B6Y3eHXo.js.map} +1 -1
  52. package/extensions/dashboard-client/dist/assets/{TimeSavedCard-CyD8e_e_.js → TimeSavedCard-BSo2lwSE.js} +2 -2
  53. package/extensions/dashboard-client/dist/assets/{TimeSavedCard-CyD8e_e_.js.map → TimeSavedCard-BSo2lwSE.js.map} +1 -1
  54. package/extensions/dashboard-client/dist/assets/{TurnMemoryView-5tLbWkRv.js → TurnMemoryView-CdGCu8Af.js} +2 -2
  55. package/extensions/dashboard-client/dist/assets/{TurnMemoryView-5tLbWkRv.js.map → TurnMemoryView-CdGCu8Af.js.map} +1 -1
  56. package/extensions/dashboard-client/dist/assets/TurnsTab-Cc4kAoAR.js +2 -0
  57. package/extensions/dashboard-client/dist/assets/{TurnsTab-C7pm92Hg.js.map → TurnsTab-Cc4kAoAR.js.map} +1 -1
  58. package/extensions/dashboard-client/dist/assets/{VcStatusBadge-ObhcIyfN.js → VcStatusBadge-DcLNLyy6.js} +2 -2
  59. package/extensions/dashboard-client/dist/assets/{VcStatusBadge-ObhcIyfN.js.map → VcStatusBadge-DcLNLyy6.js.map} +1 -1
  60. package/extensions/dashboard-client/dist/assets/{VectorCortexTab-BtHZb7o7.js → VectorCortexTab-n-3v91td.js} +2 -2
  61. package/extensions/dashboard-client/dist/assets/{VectorCortexTab-BtHZb7o7.js.map → VectorCortexTab-n-3v91td.js.map} +1 -1
  62. package/extensions/dashboard-client/dist/assets/{WikiTab-lQ4l3QIK.js → WikiTab-BpwWUleH.js} +2 -2
  63. package/extensions/dashboard-client/dist/assets/{WikiTab-lQ4l3QIK.js.map → WikiTab-BpwWUleH.js.map} +1 -1
  64. package/extensions/dashboard-client/dist/assets/{button-DhQQAJGm.js → button-B38fJDBG.js} +2 -2
  65. package/extensions/dashboard-client/dist/assets/{button-DhQQAJGm.js.map → button-B38fJDBG.js.map} +1 -1
  66. package/extensions/dashboard-client/dist/assets/{card-Cfhz9X31.js → card-COtt-Q5y.js} +2 -2
  67. package/extensions/dashboard-client/dist/assets/{card-Cfhz9X31.js.map → card-COtt-Q5y.js.map} +1 -1
  68. package/extensions/dashboard-client/dist/assets/{client-extra-CKNOGEz_.js → client-extra-D659nPqu.js} +2 -2
  69. package/extensions/dashboard-client/dist/assets/{client-extra-CKNOGEz_.js.map → client-extra-D659nPqu.js.map} +1 -1
  70. package/extensions/dashboard-client/dist/assets/{generateCategoricalChart-JKjK9_cB.js → generateCategoricalChart-COFUvM7d.js} +2 -2
  71. package/extensions/dashboard-client/dist/assets/{generateCategoricalChart-JKjK9_cB.js.map → generateCategoricalChart-COFUvM7d.js.map} +1 -1
  72. package/extensions/dashboard-client/dist/assets/{index-iuxgtzEW.js → index-C3ZUU5vU.js} +3 -3
  73. package/extensions/dashboard-client/dist/assets/{index-iuxgtzEW.js.map → index-C3ZUU5vU.js.map} +1 -1
  74. package/extensions/dashboard-client/dist/assets/{switch-7EHEQMSP.js → switch-DvYCCZ0E.js} +2 -2
  75. package/extensions/dashboard-client/dist/assets/{switch-7EHEQMSP.js.map → switch-DvYCCZ0E.js.map} +1 -1
  76. package/extensions/dashboard-client/dist/assets/{toggle-z1lWY4UM.js → toggle-bJGW8eFJ.js} +2 -2
  77. package/extensions/dashboard-client/dist/assets/{toggle-z1lWY4UM.js.map → toggle-bJGW8eFJ.js.map} +1 -1
  78. package/extensions/dashboard-client/dist/assets/{useSSE-sctOZuRM.js → useSSE-D-OoWxa2.js} +2 -2
  79. package/extensions/dashboard-client/dist/assets/{useSSE-sctOZuRM.js.map → useSSE-D-OoWxa2.js.map} +1 -1
  80. package/extensions/dashboard-client/dist/index.html +1 -1
  81. package/extensions/dashboard-client/src/components/RepoContextStack.tsx +28 -21
  82. package/extensions/dashboard-server/routes-rag-settings-helpers.ts +6 -0
  83. package/extensions/mega-config-types.ts +3 -0
  84. package/extensions/mega-config.ts +2 -0
  85. package/extensions/mega-events/analytics-handler.ts +236 -0
  86. package/extensions/mega-events/register.ts +3 -0
  87. package/package.json +1 -1
  88. package/src/queryExpansion.ts +1 -0
  89. package/src/store/analytics/connection.ts +128 -0
  90. package/src/store/analytics/contract-compliance.test/_helpers.ts +47 -0
  91. package/src/store/analytics/index.ts +61 -0
  92. package/src/store/analytics/memory-store.ts +161 -0
  93. package/src/store/analytics/schema.ts +132 -0
  94. package/src/store/analytics/sqlite-store/admin.ts +96 -0
  95. package/src/store/analytics/sqlite-store/ctx.ts +9 -0
  96. package/src/store/analytics/sqlite-store/read.ts +46 -0
  97. package/src/store/analytics/sqlite-store/rows.ts +58 -0
  98. package/src/store/analytics/sqlite-store/write.ts +111 -0
  99. package/src/store/analytics/sqlite-store.ts +119 -0
  100. package/src/store/analytics/types.ts +202 -0
  101. package/src/store/sqlite/utils.ts +7 -0
  102. package/src/store/turns/connection.ts +1 -1
  103. package/src/vector-cortex/ledger/sqlite.ts +1 -0
  104. package/extensions/dashboard-client/dist/assets/MetricsTab-CKqJ0Zl-.js +0 -2
  105. package/extensions/dashboard-client/dist/assets/OverviewTab-9Kxo79mi.js +0 -16
  106. package/extensions/dashboard-client/dist/assets/OverviewTab-9Kxo79mi.js.map +0 -1
  107. package/extensions/dashboard-client/dist/assets/TurnsTab-C7pm92Hg.js +0 -2
@@ -1 +1 @@
1
- {"version":3,"file":"ConfigTab-C016XTNC.js","sources":["../../../../src/config/themes.ts","../../src/tabs/ConfigTab.tsx"],"sourcesContent":["/**\n * themes.ts — SINGLE SOURCE OF TRUTH for game-mode visual-effect themes (S30).\n *\n * Each theme defines BOTH an HTML/CSS palette (dashboard) AND an ANSI accent\n * mapping (TUI widget), since the TUI is ANSI and the dashboard is HTML.\n *\n * Theme scope (QA1 correction): themes restyle the mega-compact statusbar\n * widget + the whole dashboard HTML page. We do NOT theme pi's chrome.\n *\n * `transparent` (default) semantics: no background fill, colored accents only —\n * so game-mode stays visible against any terminal background.\n *\n * PREVENT-PI-004: pure config, no network. Pi-agnostic: no pi runtime types.\n */\n\n/** HTML/CSS palette for the dashboard (hex strings, or `null` for \"no bg\"). */\nexport interface ThemeCss {\n /** Page/cell background hex, or `null` for transparent (no bg fill). */\n bg: string | null;\n /** Default foreground (text) hex. */\n fg: string;\n /** Accent hex (bars, highlights, level numbers). */\n accent: string;\n /** MEGA CACHE highlight hex (fires at cache >= 100%). */\n mega: string;\n}\n\n/** ANSI accent mapping for the TUI widget (SGR parameter strings, no `\\x1b[`).\n * `bg` is `null` for transparent themes (no background fill). */\nexport interface ThemeAnsi {\n /** Background SGR params (e.g. \"48;5;23\"), or `null` for no bg fill. */\n bg: string | null;\n /** Foreground SGR params (e.g. \"39\" for default, \"92\" for bright green). */\n fg: string;\n /** Accent SGR params. */\n accent: string;\n /** MEGA CACHE SGR params (fires at cache >= 100%). */\n mega: string;\n}\n\n/** A complete theme = CSS palette + ANSI mapping + display metadata. */\nexport interface Theme {\n id: string;\n label: string;\n css: ThemeCss;\n ansi: ThemeAnsi;\n}\n\nexport const DEFAULT_THEME = \"transparent\";\n\nexport const THEMES: readonly Theme[] = [\n {\n id: \"transparent\",\n label: \"Transparent (accents only)\",\n css: { bg: null, fg: \"#c9d1d9\", accent: \"#3fb950\", mega: \"#f0883e\" },\n ansi: { bg: null, fg: \"39\", accent: \"32\", mega: \"33\" },\n },\n {\n id: \"retro\",\n label: \"Retro 8-bit green\",\n css: { bg: \"#003300\", fg: \"#33ff33\", accent: \"#00ff41\", mega: \"#39ff14\" },\n ansi: { bg: \"40\", fg: \"92\", accent: \"92\", mega: \"1;92\" },\n },\n {\n id: \"orange-bold\",\n label: \"Orange bold\",\n css: { bg: \"#1a0f00\", fg: \"#ff8c00\", accent: \"#ff6b00\", mega: \"#ff4500\" },\n ansi: { bg: \"48;5;52\", fg: \"38;5;208\", accent: \"38;5;208\", mega: \"1;38;5;202\" },\n },\n {\n id: \"cyan-neon\",\n label: \"Cyan neon\",\n css: { bg: \"#001a1a\", fg: \"#00ffff\", accent: \"#22d3ee\", mega: \"#7df9ff\" },\n ansi: { bg: \"48;5;23\", fg: \"38;5;51\", accent: \"38;5;51\", mega: \"1;38;5;51\" },\n },\n {\n id: \"amber-mono\",\n label: \"Amber phosphor\",\n css: { bg: \"#1a1200\", fg: \"#ffb000\", accent: \"#ff8c00\", mega: \"#ff5500\" },\n ansi: { bg: \"48;5;58\", fg: \"38;5;214\", accent: \"38;5;214\", mega: \"1;38;5;208\" },\n },\n {\n id: \"grayscale\",\n label: \"Grayscale minimal\",\n css: { bg: \"#161616\", fg: \"#b0b0b0\", accent: \"#8b949e\", mega: \"#e6edf3\" },\n ansi: { bg: \"48;5;235\", fg: \"38;5;249\", accent: \"38;5;249\", mega: \"1;38;5;255\" },\n },\n] as const;\n\n/** All valid theme ids, in definition order (used for `theme next` cycling). */\nexport const THEME_IDS: readonly string[] = THEMES.map((t) => t.id);\n\n/** Look up a theme by id; returns `undefined` for an unknown id (caller\n * validates + falls back to DEFAULT_THEME). */\nexport function getTheme(id: string): Theme | undefined {\n return THEMES.find((t) => t.id === id);\n}\n\n/** True if `id` is a known theme id. */\nexport function isValidTheme(id: string): boolean {\n return THEME_IDS.includes(id);\n}\n\n/** Cycle to the next theme id (wraps at the end). Falls back to DEFAULT_THEME\n * if `current` is unknown (defensive — the stored id should always be valid). */\nexport function nextTheme(current: string): string {\n const i = THEME_IDS.indexOf(current);\n if (i < 0) return DEFAULT_THEME;\n return THEME_IDS[(i + 1) % THEME_IDS.length]!;\n}\n\n/** CSS variable declarations for a theme's CSS palette. `bg` is `null` for the\n * transparent theme → `--bg: transparent` (no page fill, accents only). Pure,\n * no pi types. */\nexport function themeCssVars(theme: Theme): string {\n const bg = theme.css.bg ?? \"transparent\";\n return `--bg: ${bg}; --fg: ${theme.css.fg}; --accent: ${theme.css.accent}; --mega: ${theme.css.mega};`;\n}\n\n/** A `:root[data-theme=\"<id>\"]` override block carrying the theme's CSS vars,\n * for client-side instant theme switching. Pure, no pi types. */\nexport function themeDataBlock(theme: Theme): string {\n return `:root[data-theme=\"${theme.id}\"]{ ${themeCssVars(theme)} }`;\n}\n","/**\n * dashboard-client/src/tabs/ConfigTab.tsx — Config tab (C3).\n *\n * Full configuration editor:\n * (1) Game mode toggle (checkbox)\n * (2) Theme selector (dropdown) from src/config/themes.ts THEMES array\n * (3) TUI mode selector (Full/Minimal dropdown)\n * (4) Read-only config display section from snapshot (Tier, Preset, Pressure,\n * Threshold, Fast Gate, Auto, Anchor) with verbatim tooltips from html.ts.\n *\n * Uses fetchGameState() to load current state, putGameState() to save changes.\n * Polls gameState and snapshot every 5s. Applies theme by setting\n * document.documentElement.dataset.theme.\n */\n\nimport type React from \"react\";\nimport { useState, useEffect, useCallback } from \"react\";\nimport type { SnapshotResponse, GameStatePatch } from \"@contracts\";\nimport { fetchSnapshot, fetchGameState, putGameState } from \"../api/client\";\nimport { THEMES, THEME_IDS } from \"../../../../src/config/themes\";\n\n/** Actual flat game state returned by the server (the server returns a flat\n * { game_mode_on, theme, tui_display_mode } shape, NOT the contract's nested\n * { config: { enabled, theme, displayMode }, activeRitual } shape). */\ninterface FlatGameState {\n\tgame_mode_on: boolean;\n\ttheme: string;\n\ttui_display_mode: \"full\" | \"minimal\";\n}\ntype FlatGameStatePatch = Partial<FlatGameState>;\n\n/** Extended snapshot config — the runtime includes tierPct (not in contract).\n * Implemented as a type intersection rather than `interface extends` because\n * TypeScript (TS2499) forbids `interface extends T[\"k\"]` indexed-access\n * parents; the resulting type is structurally identical. */\ntype ConfigExt = SnapshotResponse[\"config\"] & {\n\ttierPct?: number | null;\n};\n\n/* Tooltips copied verbatim from extensions/dashboard-server/html.ts */\nconst TOOLTIPS = {\n\tgameMode: \"Turn game mode on/off (themes the widget + dashboard)\",\n\ttheme: \"Visual theme (applies instantly)\",\n\ttuiMode: \"TUI widget display density\",\n\ttier: \"Live pressure band — climbs low\\u2192mega as context fills the window.\",\n\tpreset:\n\t\t\"The env-resolved base compaction preset (low/medium/high/ultra/mega) that set the token threshold.\",\n\tpressure:\n\t\t\"Live pressure = currentTokens / threshold \\u2014 % of the model context window (threshold fires at the tier's % of window).\",\n\tthreshold:\n\t\t\"Compaction threshold = tierPct \\u00d7 model context window \\u2014 mega-compact trims BELOW pi's native ~80% auto-compact for any model size.\",\n\tfastGate:\n\t\t\"Fast-gate arming floor \\u2014 the live trim arms once context passes this % of the window.\",\n} as const;\n\nfunction formatThreshold(\n\ttokens: number,\n\ttierPct: number | null | undefined,\n\tcw: number,\n): string {\n\tlet txt = tokens.toLocaleString();\n\tif (tierPct != null && cw > 0) {\n\t\ttxt += ` (${Math.round(tierPct * 100)}% of ${cw.toLocaleString()})`;\n\t}\n\treturn txt;\n}\n\nfunction formatPressure(pressure: number): string {\n\t/* Runtime sends 0\\u20131 fraction despite contract saying 0\\u2013100. */\n\treturn `${Math.round((pressure || 0) * 100)}%`;\n}\n\nexport default function ConfigTab(): React.ReactElement {\n\tconst [snapshot, setSnapshot] = useState<SnapshotResponse | null>(null);\n\tconst [gameState, setGameState] = useState<FlatGameState | null>(null);\n\tconst [saving, setSaving] = useState(false);\n\n\t/* Poll snapshot every 5s for config display section. */\n\tuseEffect(() => {\n\t\tlet active = true;\n\t\tconst doFetch = async (): Promise<void> => {\n\t\t\ttry {\n\t\t\t\tconst s = await fetchSnapshot();\n\t\t\t\tif (active) setSnapshot(s);\n\t\t\t} catch {\n\t\t\t\t/* non-fatal */\n\t\t\t}\n\t\t};\n\t\tvoid doFetch();\n\t\tconst timer = setInterval(() => void doFetch(), 5000);\n\t\treturn () => {\n\t\t\tactive = false;\n\t\t\tclearInterval(timer);\n\t\t};\n\t}, []);\n\n\t/* Poll game state every 5s. */\n\tuseEffect(() => {\n\t\tlet active = true;\n\t\tconst doFetch = async (): Promise<void> => {\n\t\t\ttry {\n\t\t\t\tconst gs = (await fetchGameState()) as unknown as FlatGameState;\n\t\t\t\tif (active) {\n\t\t\t\t\tsetGameState(gs);\n\t\t\t\t\tif (gs.theme)\n\t\t\t\t\t\tdocument.documentElement.dataset.theme = gs.theme;\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t/* non-fatal */\n\t\t\t}\n\t\t};\n\t\tvoid doFetch();\n\t\tconst timer = setInterval(() => void doFetch(), 5000);\n\t\treturn () => {\n\t\t\tactive = false;\n\t\t\tclearInterval(timer);\n\t\t};\n\t}, []);\n\n\t/* PUT a patch to the server immediately on change. */\n\tconst handlePatch = useCallback(async (patch: FlatGameStatePatch) => {\n\t\tsetSaving(true);\n\t\ttry {\n\t\t\tconst result = (await putGameState(\n\t\t\t\tpatch as unknown as GameStatePatch,\n\t\t\t)) as unknown as FlatGameState;\n\t\t\tsetGameState(result);\n\t\t\tif (result.theme)\n\t\t\t\tdocument.documentElement.dataset.theme = result.theme;\n\t\t} catch {\n\t\t\t/* non-fatal */\n\t\t}\n\t\tsetSaving(false);\n\t}, []);\n\n\tconst gmOn = gameState?.game_mode_on ?? false;\n\tconst themeVal = gameState?.theme ?? \"transparent\";\n\tconst tuiVal = gameState?.tui_display_mode ?? \"full\";\n\n\tconst cfg = snapshot?.config as ConfigExt | undefined;\n\tconst cw = snapshot?.context.contextWindow ?? 0;\n\n\treturn (\n\t\t<div className=\"config-tab\">\n\t\t\t<section className=\"config-settings\">\n\t\t\t\t<h3>Settings</h3>\n\t\t\t\t<div className=\"settings-row\">\n\t\t\t\t\t<label title={TOOLTIPS.gameMode}>\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\t\tchecked={gmOn}\n\t\t\t\t\t\t\tonChange={(e) =>\n\t\t\t\t\t\t\t\thandlePatch({ game_mode_on: e.currentTarget.checked })\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tdisabled={saving}\n\t\t\t\t\t\t/>\n\t\t\t\t\t\tGame Mode\n\t\t\t\t\t</label>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"settings-row\">\n\t\t\t\t\t<label title={TOOLTIPS.theme}>\n\t\t\t\t\t\tTheme\n\t\t\t\t\t\t<select\n\t\t\t\t\t\t\tvalue={themeVal}\n\t\t\t\t\t\t\tonChange={(e) => handlePatch({ theme: e.currentTarget.value })}\n\t\t\t\t\t\t\tdisabled={saving}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{THEMES.map((t) => (\n\t\t\t\t\t\t\t\t<option key={t.id} value={t.id}>\n\t\t\t\t\t\t\t\t\t{t.label}\n\t\t\t\t\t\t\t\t</option>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</select>\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\tdisabled={saving}\n\t\t\t\t\t\t\tclassName=\"theme-cycle-btn\"\n\t\t\t\t\t\t\ttitle=\"Cycle to the next theme (P3)\"\n\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\tconst i = THEME_IDS.indexOf(themeVal);\n\t\t\t\t\t\t\t\tconst next =\n\t\t\t\t\t\t\t\t\tTHEME_IDS[(i < 0 ? 0 : i + 1) % THEME_IDS.length];\n\t\t\t\t\t\t\t\tif (next) void handlePatch({ theme: next });\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\tNext theme →\n\t\t\t\t\t\t</button>\n\t\t\t\t\t</label>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"settings-row\">\n\t\t\t\t\t<label title={TOOLTIPS.tuiMode}>\n\t\t\t\t\t\tTUI Mode\n\t\t\t\t\t\t<select\n\t\t\t\t\t\t\tvalue={tuiVal}\n\t\t\t\t\t\t\tonChange={(e) =>\n\t\t\t\t\t\t\t\thandlePatch({\n\t\t\t\t\t\t\t\t\ttui_display_mode: e.currentTarget.value as \"full\" | \"minimal\",\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tdisabled={saving}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<option value=\"full\">Full</option>\n\t\t\t\t\t\t\t<option value=\"minimal\">Minimal</option>\n\t\t\t\t\t\t</select>{\" \"}\n\t\t\t\t\t\t\t<span className=\"tui-mode-hint\">\n\t\t\t\t\t\t\t\t(affects the in-app TUI widget; not shown here)\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t</label>\n\t\t\t\t</div>\n\t\t\t</section>\n\n\t\t\t<section className=\"config-display\">\n\t\t\t\t<h3>Configuration</h3>\n\t\t\t\t<div className=\"config-grid\">\n\t\t\t\t\t<div className=\"config-item\">\n\t\t\t\t\t\t<span className=\"config-label\" title={TOOLTIPS.tier}>\n\t\t\t\t\t\t\tTier\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span className=\"config-value\">\n\t\t\t\t\t\t\t{snapshot ? `${snapshot.tier} (live)` : \"\\u2014\"}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"config-item\">\n\t\t\t\t\t\t<span className=\"config-label\" title={TOOLTIPS.preset}>\n\t\t\t\t\t\t\tPreset\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span className=\"config-value\">\n\t\t\t\t\t\t\t{snapshot?.presetTier ?? \"\\u2014\"}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"config-item\">\n\t\t\t\t\t\t<span className=\"config-label\" title={TOOLTIPS.pressure}>\n\t\t\t\t\t\t\tPressure\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span className=\"config-value\">\n\t\t\t\t\t\t\t{snapshot ? formatPressure(snapshot.pressure) : \"\\u2014\"}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"config-item\">\n\t\t\t\t\t\t<span className=\"config-label\" title={TOOLTIPS.threshold}>\n\t\t\t\t\t\t\tThreshold\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span className=\"config-value\">\n\t\t\t\t\t\t\t{cfg ? formatThreshold(cfg.thresholdTokens, cfg.tierPct, cw) : \"\\u2014\"}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"config-item\">\n\t\t\t\t\t\t<span className=\"config-label\" title={TOOLTIPS.fastGate}>\n\t\t\t\t\t\t\tFast Gate\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span className=\"config-value\">\n\t\t\t\t\t\t\t{cfg ? `${cfg.fastGatePct}%` : \"\\u2014\"}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"config-item\">\n\t\t\t\t\t\t<span className=\"config-label\">Auto</span>\n\t\t\t\t\t\t<span className=\"config-value\">\n\t\t\t\t\t\t\t{cfg ? (cfg.auto ? \"enabled\" : \"disabled\") : \"\\u2014\"}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"config-item\">\n\t\t\t\t\t\t<span className=\"config-label\">Anchor</span>\n\t\t\t\t\t\t<span className=\"config-value\">\n\t\t\t\t\t\t\t{cfg?.anchorUserMessages ?? \"\\u2014\"}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</section>\n\t\t</div>\n\t);\n}\n"],"names":["THEMES","THEME_IDS","TOOLTIPS","formatThreshold","tokens","tierPct","cw","txt","formatPressure","pressure","ConfigTab","snapshot","setSnapshot","useState","gameState","setGameState","saving","setSaving","useEffect","active","doFetch","s","fetchSnapshot","timer","gs","fetchGameState","handlePatch","useCallback","patch","result","putGameState","gmOn","themeVal","tuiVal","cfg","jsxs","jsx","e","t","i","next"],"mappings":"oEAkDO,MAAMA,EAA2B,CACtC,CACE,GAAI,cACJ,MAAO,6BACP,IAAK,CAAE,GAAI,KAAM,GAAI,UAAW,OAAQ,UAAW,KAAM,SAAA,EACzD,KAAM,CAAE,GAAI,KAAM,GAAI,KAAM,OAAQ,KAAM,KAAM,IAAA,CAAK,EAEvD,CACE,GAAI,QACJ,MAAO,oBACP,IAAK,CAAE,GAAI,UAAW,GAAI,UAAW,OAAQ,UAAW,KAAM,SAAA,EAC9D,KAAM,CAAE,GAAI,KAAM,GAAI,KAAM,OAAQ,KAAM,KAAM,MAAA,CAAO,EAEzD,CACE,GAAI,cACJ,MAAO,cACP,IAAK,CAAE,GAAI,UAAW,GAAI,UAAW,OAAQ,UAAW,KAAM,SAAA,EAC9D,KAAM,CAAE,GAAI,UAAW,GAAI,WAAY,OAAQ,WAAY,KAAM,YAAA,CAAa,EAEhF,CACE,GAAI,YACJ,MAAO,YACP,IAAK,CAAE,GAAI,UAAW,GAAI,UAAW,OAAQ,UAAW,KAAM,SAAA,EAC9D,KAAM,CAAE,GAAI,UAAW,GAAI,UAAW,OAAQ,UAAW,KAAM,WAAA,CAAY,EAE7E,CACE,GAAI,aACJ,MAAO,iBACP,IAAK,CAAE,GAAI,UAAW,GAAI,UAAW,OAAQ,UAAW,KAAM,SAAA,EAC9D,KAAM,CAAE,GAAI,UAAW,GAAI,WAAY,OAAQ,WAAY,KAAM,YAAA,CAAa,EAEhF,CACE,GAAI,YACJ,MAAO,oBACP,IAAK,CAAE,GAAI,UAAW,GAAI,UAAW,OAAQ,UAAW,KAAM,SAAA,EAC9D,KAAM,CAAE,GAAI,WAAY,GAAI,WAAY,OAAQ,WAAY,KAAM,YAAA,CAAa,CAEnF,EAGaC,EAA+BD,EAAO,IAAK,GAAM,EAAE,EAAE,EClD5DE,EAAW,CAChB,SAAU,wDACV,MAAO,mCACP,QAAS,6BACT,KAAM,oEACN,OACC,qGACD,SACC,yHACD,UACC,qIACD,SACC,uFACF,EAEA,SAASC,EACRC,EACAC,EACAC,EACS,CACT,IAAIC,EAAMH,EAAO,eAAA,EACjB,OAAIC,GAAW,MAAQC,EAAK,IAC3BC,GAAO,KAAK,KAAK,MAAMF,EAAU,GAAG,CAAC,QAAQC,EAAG,eAAA,CAAgB,KAE1DC,CACR,CAEA,SAASC,EAAeC,EAA0B,CAEjD,MAAO,GAAG,KAAK,OAAOA,GAAY,GAAK,GAAG,CAAC,GAC5C,CAEA,SAAwBC,GAAgC,CACvD,KAAM,CAACC,EAAUC,CAAW,EAAIC,EAAAA,SAAkC,IAAI,EAChE,CAACC,EAAWC,CAAY,EAAIF,EAAAA,SAA+B,IAAI,EAC/D,CAACG,EAAQC,CAAS,EAAIJ,EAAAA,SAAS,EAAK,EAG1CK,EAAAA,UAAU,IAAM,CACf,IAAIC,EAAS,GACb,MAAMC,EAAU,SAA2B,CAC1C,GAAI,CACH,MAAMC,EAAI,MAAMC,EAAA,EACZH,KAAoBE,CAAC,CAC1B,MAAQ,CAER,CACD,EACKD,EAAA,EACL,MAAMG,EAAQ,YAAY,IAAM,KAAKH,EAAA,EAAW,GAAI,EACpD,MAAO,IAAM,CACZD,EAAS,GACT,cAAcI,CAAK,CACpB,CACD,EAAG,CAAA,CAAE,EAGLL,EAAAA,UAAU,IAAM,CACf,IAAIC,EAAS,GACb,MAAMC,EAAU,SAA2B,CAC1C,GAAI,CACH,MAAMI,EAAM,MAAMC,EAAA,EACdN,IACHJ,EAAaS,CAAE,EACXA,EAAG,QACN,SAAS,gBAAgB,QAAQ,MAAQA,EAAG,OAE/C,MAAQ,CAER,CACD,EACKJ,EAAA,EACL,MAAMG,EAAQ,YAAY,IAAM,KAAKH,EAAA,EAAW,GAAI,EACpD,MAAO,IAAM,CACZD,EAAS,GACT,cAAcI,CAAK,CACpB,CACD,EAAG,CAAA,CAAE,EAGL,MAAMG,EAAcC,cAAY,MAAOC,GAA8B,CACpEX,EAAU,EAAI,EACd,GAAI,CACH,MAAMY,EAAU,MAAMC,EACrBF,CAAA,EAEDb,EAAac,CAAM,EACfA,EAAO,QACV,SAAS,gBAAgB,QAAQ,MAAQA,EAAO,MAClD,MAAQ,CAER,CACAZ,EAAU,EAAK,CAChB,EAAG,CAAA,CAAE,EAECc,EAAOjB,GAAW,cAAgB,GAClCkB,EAAWlB,GAAW,OAAS,cAC/BmB,EAASnB,GAAW,kBAAoB,OAExCoB,EAAMvB,GAAU,OAChBL,EAAKK,GAAU,QAAQ,eAAiB,EAE9C,OACCwB,EAAAA,KAAC,MAAA,CAAI,UAAU,aACd,SAAA,CAAAA,EAAAA,KAAC,UAAA,CAAQ,UAAU,kBAClB,SAAA,CAAAC,EAAAA,IAAC,MAAG,SAAA,UAAA,CAAQ,EACZA,EAAAA,IAAC,OAAI,UAAU,eACd,gBAAC,QAAA,CAAM,MAAOlC,EAAS,SACtB,SAAA,CAAAkC,EAAAA,IAAC,QAAA,CACA,KAAK,WACL,QAASL,EACT,SAAWM,GACVX,EAAY,CAAE,aAAcW,EAAE,cAAc,QAAS,EAEtD,SAAUrB,CAAA,CAAA,EACT,WAAA,CAAA,CAEH,CAAA,CACD,EACAoB,EAAAA,IAAC,OAAI,UAAU,eACd,gBAAC,QAAA,CAAM,MAAOlC,EAAS,MAAO,SAAA,CAAA,QAE7BkC,EAAAA,IAAC,SAAA,CACA,MAAOJ,EACP,SAAWK,GAAMX,EAAY,CAAE,MAAOW,EAAE,cAAc,MAAO,EAC7D,SAAUrB,EAET,SAAAhB,EAAO,IAAKsC,GACZF,EAAAA,IAAC,SAAA,CAAkB,MAAOE,EAAE,GAC1B,SAAAA,EAAE,KAAA,EADSA,EAAE,EAEf,CACA,CAAA,CAAA,EAEFF,EAAAA,IAAC,SAAA,CACA,KAAK,SACL,SAAUpB,EACV,UAAU,kBACV,MAAM,+BACN,QAAS,IAAM,CACd,MAAMuB,EAAItC,EAAU,QAAQ+B,CAAQ,EAC9BQ,EACLvC,GAAWsC,EAAI,EAAI,EAAIA,EAAI,GAAKtC,EAAU,MAAM,EAC7CuC,GAAWd,EAAY,CAAE,MAAOc,EAAM,CAC3C,EACA,SAAA,cAAA,CAAA,CAED,CAAA,CACD,CAAA,CACD,EACAJ,EAAAA,IAAC,OAAI,UAAU,eACd,gBAAC,QAAA,CAAM,MAAOlC,EAAS,QAAS,SAAA,CAAA,WAE/BiC,EAAAA,KAAC,SAAA,CACA,MAAOF,EACP,SAAWI,GACVX,EAAY,CACX,iBAAkBW,EAAE,cAAc,KAAA,CAClC,EAEF,SAAUrB,EAEV,SAAA,CAAAoB,EAAAA,IAAC,SAAA,CAAO,MAAM,OAAO,SAAA,OAAI,EACzBA,EAAAA,IAAC,SAAA,CAAO,MAAM,UAAU,SAAA,SAAA,CAAO,CAAA,CAAA,CAAA,EACtB,IACTA,EAAAA,IAAC,OAAA,CAAK,UAAU,gBAAgB,SAAA,iDAAA,CAEhC,CAAA,CAAA,CACF,CAAA,CACD,CAAA,EACD,EAEAD,EAAAA,KAAC,UAAA,CAAQ,UAAU,iBAClB,SAAA,CAAAC,EAAAA,IAAC,MAAG,SAAA,eAAA,CAAa,EACjBD,EAAAA,KAAC,MAAA,CAAI,UAAU,cACd,SAAA,CAAAA,EAAAA,KAAC,MAAA,CAAI,UAAU,cACd,SAAA,CAAAC,MAAC,QAAK,UAAU,eAAe,MAAOlC,EAAS,KAAM,SAAA,OAErD,EACAkC,EAAAA,IAAC,QAAK,UAAU,eACd,WAAW,GAAGzB,EAAS,IAAI,UAAY,GAAA,CACzC,CAAA,EACD,EACAwB,EAAAA,KAAC,MAAA,CAAI,UAAU,cACd,SAAA,CAAAC,MAAC,QAAK,UAAU,eAAe,MAAOlC,EAAS,OAAQ,SAAA,SAEvD,QACC,OAAA,CAAK,UAAU,eACd,SAAAS,GAAU,YAAc,GAAA,CAC1B,CAAA,EACD,EACAwB,EAAAA,KAAC,MAAA,CAAI,UAAU,cACd,SAAA,CAAAC,MAAC,QAAK,UAAU,eAAe,MAAOlC,EAAS,SAAU,SAAA,WAEzD,EACAkC,EAAAA,IAAC,QAAK,UAAU,eACd,WAAW5B,EAAeG,EAAS,QAAQ,EAAI,GAAA,CACjD,CAAA,EACD,EACAwB,EAAAA,KAAC,MAAA,CAAI,UAAU,cACd,SAAA,CAAAC,MAAC,QAAK,UAAU,eAAe,MAAOlC,EAAS,UAAW,SAAA,YAE1D,EACAkC,EAAAA,IAAC,OAAA,CAAK,UAAU,eACd,SAAAF,EAAM/B,EAAgB+B,EAAI,gBAAiBA,EAAI,QAAS5B,CAAE,EAAI,GAAA,CAChE,CAAA,EACD,EACA6B,EAAAA,KAAC,MAAA,CAAI,UAAU,cACd,SAAA,CAAAC,MAAC,QAAK,UAAU,eAAe,MAAOlC,EAAS,SAAU,SAAA,YAEzD,EACAkC,EAAAA,IAAC,QAAK,UAAU,eACd,WAAM,GAAGF,EAAI,WAAW,IAAM,GAAA,CAChC,CAAA,EACD,EACAC,EAAAA,KAAC,MAAA,CAAI,UAAU,cACd,SAAA,CAAAC,EAAAA,IAAC,OAAA,CAAK,UAAU,eAAe,SAAA,OAAI,EACnCA,EAAAA,IAAC,QAAK,UAAU,eACd,WAAOF,EAAI,KAAO,UAAY,WAAc,GAAA,CAC9C,CAAA,EACD,EACAC,EAAAA,KAAC,MAAA,CAAI,UAAU,cACd,SAAA,CAAAC,EAAAA,IAAC,OAAA,CAAK,UAAU,eAAe,SAAA,SAAM,QACpC,OAAA,CAAK,UAAU,eACd,SAAAF,GAAK,oBAAsB,GAAA,CAC7B,CAAA,CAAA,CACD,CAAA,CAAA,CACD,CAAA,CAAA,CACD,CAAA,EACD,CAEF"}
1
+ {"version":3,"file":"ConfigTab-Ci5LOohz.js","sources":["../../../../src/config/themes.ts","../../src/tabs/ConfigTab.tsx"],"sourcesContent":["/**\n * themes.ts — SINGLE SOURCE OF TRUTH for game-mode visual-effect themes (S30).\n *\n * Each theme defines BOTH an HTML/CSS palette (dashboard) AND an ANSI accent\n * mapping (TUI widget), since the TUI is ANSI and the dashboard is HTML.\n *\n * Theme scope (QA1 correction): themes restyle the mega-compact statusbar\n * widget + the whole dashboard HTML page. We do NOT theme pi's chrome.\n *\n * `transparent` (default) semantics: no background fill, colored accents only —\n * so game-mode stays visible against any terminal background.\n *\n * PREVENT-PI-004: pure config, no network. Pi-agnostic: no pi runtime types.\n */\n\n/** HTML/CSS palette for the dashboard (hex strings, or `null` for \"no bg\"). */\nexport interface ThemeCss {\n /** Page/cell background hex, or `null` for transparent (no bg fill). */\n bg: string | null;\n /** Default foreground (text) hex. */\n fg: string;\n /** Accent hex (bars, highlights, level numbers). */\n accent: string;\n /** MEGA CACHE highlight hex (fires at cache >= 100%). */\n mega: string;\n}\n\n/** ANSI accent mapping for the TUI widget (SGR parameter strings, no `\\x1b[`).\n * `bg` is `null` for transparent themes (no background fill). */\nexport interface ThemeAnsi {\n /** Background SGR params (e.g. \"48;5;23\"), or `null` for no bg fill. */\n bg: string | null;\n /** Foreground SGR params (e.g. \"39\" for default, \"92\" for bright green). */\n fg: string;\n /** Accent SGR params. */\n accent: string;\n /** MEGA CACHE SGR params (fires at cache >= 100%). */\n mega: string;\n}\n\n/** A complete theme = CSS palette + ANSI mapping + display metadata. */\nexport interface Theme {\n id: string;\n label: string;\n css: ThemeCss;\n ansi: ThemeAnsi;\n}\n\nexport const DEFAULT_THEME = \"transparent\";\n\nexport const THEMES: readonly Theme[] = [\n {\n id: \"transparent\",\n label: \"Transparent (accents only)\",\n css: { bg: null, fg: \"#c9d1d9\", accent: \"#3fb950\", mega: \"#f0883e\" },\n ansi: { bg: null, fg: \"39\", accent: \"32\", mega: \"33\" },\n },\n {\n id: \"retro\",\n label: \"Retro 8-bit green\",\n css: { bg: \"#003300\", fg: \"#33ff33\", accent: \"#00ff41\", mega: \"#39ff14\" },\n ansi: { bg: \"40\", fg: \"92\", accent: \"92\", mega: \"1;92\" },\n },\n {\n id: \"orange-bold\",\n label: \"Orange bold\",\n css: { bg: \"#1a0f00\", fg: \"#ff8c00\", accent: \"#ff6b00\", mega: \"#ff4500\" },\n ansi: { bg: \"48;5;52\", fg: \"38;5;208\", accent: \"38;5;208\", mega: \"1;38;5;202\" },\n },\n {\n id: \"cyan-neon\",\n label: \"Cyan neon\",\n css: { bg: \"#001a1a\", fg: \"#00ffff\", accent: \"#22d3ee\", mega: \"#7df9ff\" },\n ansi: { bg: \"48;5;23\", fg: \"38;5;51\", accent: \"38;5;51\", mega: \"1;38;5;51\" },\n },\n {\n id: \"amber-mono\",\n label: \"Amber phosphor\",\n css: { bg: \"#1a1200\", fg: \"#ffb000\", accent: \"#ff8c00\", mega: \"#ff5500\" },\n ansi: { bg: \"48;5;58\", fg: \"38;5;214\", accent: \"38;5;214\", mega: \"1;38;5;208\" },\n },\n {\n id: \"grayscale\",\n label: \"Grayscale minimal\",\n css: { bg: \"#161616\", fg: \"#b0b0b0\", accent: \"#8b949e\", mega: \"#e6edf3\" },\n ansi: { bg: \"48;5;235\", fg: \"38;5;249\", accent: \"38;5;249\", mega: \"1;38;5;255\" },\n },\n] as const;\n\n/** All valid theme ids, in definition order (used for `theme next` cycling). */\nexport const THEME_IDS: readonly string[] = THEMES.map((t) => t.id);\n\n/** Look up a theme by id; returns `undefined` for an unknown id (caller\n * validates + falls back to DEFAULT_THEME). */\nexport function getTheme(id: string): Theme | undefined {\n return THEMES.find((t) => t.id === id);\n}\n\n/** True if `id` is a known theme id. */\nexport function isValidTheme(id: string): boolean {\n return THEME_IDS.includes(id);\n}\n\n/** Cycle to the next theme id (wraps at the end). Falls back to DEFAULT_THEME\n * if `current` is unknown (defensive — the stored id should always be valid). */\nexport function nextTheme(current: string): string {\n const i = THEME_IDS.indexOf(current);\n if (i < 0) return DEFAULT_THEME;\n return THEME_IDS[(i + 1) % THEME_IDS.length]!;\n}\n\n/** CSS variable declarations for a theme's CSS palette. `bg` is `null` for the\n * transparent theme → `--bg: transparent` (no page fill, accents only). Pure,\n * no pi types. */\nexport function themeCssVars(theme: Theme): string {\n const bg = theme.css.bg ?? \"transparent\";\n return `--bg: ${bg}; --fg: ${theme.css.fg}; --accent: ${theme.css.accent}; --mega: ${theme.css.mega};`;\n}\n\n/** A `:root[data-theme=\"<id>\"]` override block carrying the theme's CSS vars,\n * for client-side instant theme switching. Pure, no pi types. */\nexport function themeDataBlock(theme: Theme): string {\n return `:root[data-theme=\"${theme.id}\"]{ ${themeCssVars(theme)} }`;\n}\n","/**\n * dashboard-client/src/tabs/ConfigTab.tsx — Config tab (C3).\n *\n * Full configuration editor:\n * (1) Game mode toggle (checkbox)\n * (2) Theme selector (dropdown) from src/config/themes.ts THEMES array\n * (3) TUI mode selector (Full/Minimal dropdown)\n * (4) Read-only config display section from snapshot (Tier, Preset, Pressure,\n * Threshold, Fast Gate, Auto, Anchor) with verbatim tooltips from html.ts.\n *\n * Uses fetchGameState() to load current state, putGameState() to save changes.\n * Polls gameState and snapshot every 5s. Applies theme by setting\n * document.documentElement.dataset.theme.\n */\n\nimport type React from \"react\";\nimport { useState, useEffect, useCallback } from \"react\";\nimport type { SnapshotResponse, GameStatePatch } from \"@contracts\";\nimport { fetchSnapshot, fetchGameState, putGameState } from \"../api/client\";\nimport { THEMES, THEME_IDS } from \"../../../../src/config/themes\";\n\n/** Actual flat game state returned by the server (the server returns a flat\n * { game_mode_on, theme, tui_display_mode } shape, NOT the contract's nested\n * { config: { enabled, theme, displayMode }, activeRitual } shape). */\ninterface FlatGameState {\n\tgame_mode_on: boolean;\n\ttheme: string;\n\ttui_display_mode: \"full\" | \"minimal\";\n}\ntype FlatGameStatePatch = Partial<FlatGameState>;\n\n/** Extended snapshot config — the runtime includes tierPct (not in contract).\n * Implemented as a type intersection rather than `interface extends` because\n * TypeScript (TS2499) forbids `interface extends T[\"k\"]` indexed-access\n * parents; the resulting type is structurally identical. */\ntype ConfigExt = SnapshotResponse[\"config\"] & {\n\ttierPct?: number | null;\n};\n\n/* Tooltips copied verbatim from extensions/dashboard-server/html.ts */\nconst TOOLTIPS = {\n\tgameMode: \"Turn game mode on/off (themes the widget + dashboard)\",\n\ttheme: \"Visual theme (applies instantly)\",\n\ttuiMode: \"TUI widget display density\",\n\ttier: \"Live pressure band — climbs low\\u2192mega as context fills the window.\",\n\tpreset:\n\t\t\"The env-resolved base compaction preset (low/medium/high/ultra/mega) that set the token threshold.\",\n\tpressure:\n\t\t\"Live pressure = currentTokens / threshold \\u2014 % of the model context window (threshold fires at the tier's % of window).\",\n\tthreshold:\n\t\t\"Compaction threshold = tierPct \\u00d7 model context window \\u2014 mega-compact trims BELOW pi's native ~80% auto-compact for any model size.\",\n\tfastGate:\n\t\t\"Fast-gate arming floor \\u2014 the live trim arms once context passes this % of the window.\",\n} as const;\n\nfunction formatThreshold(\n\ttokens: number,\n\ttierPct: number | null | undefined,\n\tcw: number,\n): string {\n\tlet txt = tokens.toLocaleString();\n\tif (tierPct != null && cw > 0) {\n\t\ttxt += ` (${Math.round(tierPct * 100)}% of ${cw.toLocaleString()})`;\n\t}\n\treturn txt;\n}\n\nfunction formatPressure(pressure: number): string {\n\t/* Runtime sends 0\\u20131 fraction despite contract saying 0\\u2013100. */\n\treturn `${Math.round((pressure || 0) * 100)}%`;\n}\n\nexport default function ConfigTab(): React.ReactElement {\n\tconst [snapshot, setSnapshot] = useState<SnapshotResponse | null>(null);\n\tconst [gameState, setGameState] = useState<FlatGameState | null>(null);\n\tconst [saving, setSaving] = useState(false);\n\n\t/* Poll snapshot every 5s for config display section. */\n\tuseEffect(() => {\n\t\tlet active = true;\n\t\tconst doFetch = async (): Promise<void> => {\n\t\t\ttry {\n\t\t\t\tconst s = await fetchSnapshot();\n\t\t\t\tif (active) setSnapshot(s);\n\t\t\t} catch {\n\t\t\t\t/* non-fatal */\n\t\t\t}\n\t\t};\n\t\tvoid doFetch();\n\t\tconst timer = setInterval(() => void doFetch(), 5000);\n\t\treturn () => {\n\t\t\tactive = false;\n\t\t\tclearInterval(timer);\n\t\t};\n\t}, []);\n\n\t/* Poll game state every 5s. */\n\tuseEffect(() => {\n\t\tlet active = true;\n\t\tconst doFetch = async (): Promise<void> => {\n\t\t\ttry {\n\t\t\t\tconst gs = (await fetchGameState()) as unknown as FlatGameState;\n\t\t\t\tif (active) {\n\t\t\t\t\tsetGameState(gs);\n\t\t\t\t\tif (gs.theme)\n\t\t\t\t\t\tdocument.documentElement.dataset.theme = gs.theme;\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t/* non-fatal */\n\t\t\t}\n\t\t};\n\t\tvoid doFetch();\n\t\tconst timer = setInterval(() => void doFetch(), 5000);\n\t\treturn () => {\n\t\t\tactive = false;\n\t\t\tclearInterval(timer);\n\t\t};\n\t}, []);\n\n\t/* PUT a patch to the server immediately on change. */\n\tconst handlePatch = useCallback(async (patch: FlatGameStatePatch) => {\n\t\tsetSaving(true);\n\t\ttry {\n\t\t\tconst result = (await putGameState(\n\t\t\t\tpatch as unknown as GameStatePatch,\n\t\t\t)) as unknown as FlatGameState;\n\t\t\tsetGameState(result);\n\t\t\tif (result.theme)\n\t\t\t\tdocument.documentElement.dataset.theme = result.theme;\n\t\t} catch {\n\t\t\t/* non-fatal */\n\t\t}\n\t\tsetSaving(false);\n\t}, []);\n\n\tconst gmOn = gameState?.game_mode_on ?? false;\n\tconst themeVal = gameState?.theme ?? \"transparent\";\n\tconst tuiVal = gameState?.tui_display_mode ?? \"full\";\n\n\tconst cfg = snapshot?.config as ConfigExt | undefined;\n\tconst cw = snapshot?.context.contextWindow ?? 0;\n\n\treturn (\n\t\t<div className=\"config-tab\">\n\t\t\t<section className=\"config-settings\">\n\t\t\t\t<h3>Settings</h3>\n\t\t\t\t<div className=\"settings-row\">\n\t\t\t\t\t<label title={TOOLTIPS.gameMode}>\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\t\tchecked={gmOn}\n\t\t\t\t\t\t\tonChange={(e) =>\n\t\t\t\t\t\t\t\thandlePatch({ game_mode_on: e.currentTarget.checked })\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tdisabled={saving}\n\t\t\t\t\t\t/>\n\t\t\t\t\t\tGame Mode\n\t\t\t\t\t</label>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"settings-row\">\n\t\t\t\t\t<label title={TOOLTIPS.theme}>\n\t\t\t\t\t\tTheme\n\t\t\t\t\t\t<select\n\t\t\t\t\t\t\tvalue={themeVal}\n\t\t\t\t\t\t\tonChange={(e) => handlePatch({ theme: e.currentTarget.value })}\n\t\t\t\t\t\t\tdisabled={saving}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{THEMES.map((t) => (\n\t\t\t\t\t\t\t\t<option key={t.id} value={t.id}>\n\t\t\t\t\t\t\t\t\t{t.label}\n\t\t\t\t\t\t\t\t</option>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</select>\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\tdisabled={saving}\n\t\t\t\t\t\t\tclassName=\"theme-cycle-btn\"\n\t\t\t\t\t\t\ttitle=\"Cycle to the next theme (P3)\"\n\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\tconst i = THEME_IDS.indexOf(themeVal);\n\t\t\t\t\t\t\t\tconst next =\n\t\t\t\t\t\t\t\t\tTHEME_IDS[(i < 0 ? 0 : i + 1) % THEME_IDS.length];\n\t\t\t\t\t\t\t\tif (next) void handlePatch({ theme: next });\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\tNext theme →\n\t\t\t\t\t\t</button>\n\t\t\t\t\t</label>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"settings-row\">\n\t\t\t\t\t<label title={TOOLTIPS.tuiMode}>\n\t\t\t\t\t\tTUI Mode\n\t\t\t\t\t\t<select\n\t\t\t\t\t\t\tvalue={tuiVal}\n\t\t\t\t\t\t\tonChange={(e) =>\n\t\t\t\t\t\t\t\thandlePatch({\n\t\t\t\t\t\t\t\t\ttui_display_mode: e.currentTarget.value as \"full\" | \"minimal\",\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tdisabled={saving}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<option value=\"full\">Full</option>\n\t\t\t\t\t\t\t<option value=\"minimal\">Minimal</option>\n\t\t\t\t\t\t</select>{\" \"}\n\t\t\t\t\t\t\t<span className=\"tui-mode-hint\">\n\t\t\t\t\t\t\t\t(affects the in-app TUI widget; not shown here)\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t</label>\n\t\t\t\t</div>\n\t\t\t</section>\n\n\t\t\t<section className=\"config-display\">\n\t\t\t\t<h3>Configuration</h3>\n\t\t\t\t<div className=\"config-grid\">\n\t\t\t\t\t<div className=\"config-item\">\n\t\t\t\t\t\t<span className=\"config-label\" title={TOOLTIPS.tier}>\n\t\t\t\t\t\t\tTier\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span className=\"config-value\">\n\t\t\t\t\t\t\t{snapshot ? `${snapshot.tier} (live)` : \"\\u2014\"}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"config-item\">\n\t\t\t\t\t\t<span className=\"config-label\" title={TOOLTIPS.preset}>\n\t\t\t\t\t\t\tPreset\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span className=\"config-value\">\n\t\t\t\t\t\t\t{snapshot?.presetTier ?? \"\\u2014\"}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"config-item\">\n\t\t\t\t\t\t<span className=\"config-label\" title={TOOLTIPS.pressure}>\n\t\t\t\t\t\t\tPressure\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span className=\"config-value\">\n\t\t\t\t\t\t\t{snapshot ? formatPressure(snapshot.pressure) : \"\\u2014\"}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"config-item\">\n\t\t\t\t\t\t<span className=\"config-label\" title={TOOLTIPS.threshold}>\n\t\t\t\t\t\t\tThreshold\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span className=\"config-value\">\n\t\t\t\t\t\t\t{cfg ? formatThreshold(cfg.thresholdTokens, cfg.tierPct, cw) : \"\\u2014\"}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"config-item\">\n\t\t\t\t\t\t<span className=\"config-label\" title={TOOLTIPS.fastGate}>\n\t\t\t\t\t\t\tFast Gate\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span className=\"config-value\">\n\t\t\t\t\t\t\t{cfg ? `${cfg.fastGatePct}%` : \"\\u2014\"}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"config-item\">\n\t\t\t\t\t\t<span className=\"config-label\">Auto</span>\n\t\t\t\t\t\t<span className=\"config-value\">\n\t\t\t\t\t\t\t{cfg ? (cfg.auto ? \"enabled\" : \"disabled\") : \"\\u2014\"}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"config-item\">\n\t\t\t\t\t\t<span className=\"config-label\">Anchor</span>\n\t\t\t\t\t\t<span className=\"config-value\">\n\t\t\t\t\t\t\t{cfg?.anchorUserMessages ?? \"\\u2014\"}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</section>\n\t\t</div>\n\t);\n}\n"],"names":["THEMES","THEME_IDS","TOOLTIPS","formatThreshold","tokens","tierPct","cw","txt","formatPressure","pressure","ConfigTab","snapshot","setSnapshot","useState","gameState","setGameState","saving","setSaving","useEffect","active","doFetch","s","fetchSnapshot","timer","gs","fetchGameState","handlePatch","useCallback","patch","result","putGameState","gmOn","themeVal","tuiVal","cfg","jsxs","jsx","e","t","i","next"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import{r as o,j as r,B as g}from"./index-iuxgtzEW.js";import{u as m}from"./useSSE-sctOZuRM.js";import{T as u}from"./toggle-z1lWY4UM.js";const p={compact_start:"ev-compact",compact_end:"ev-compact",compact_trigger:"ev-trigger",compact_skip:"ev-trigger",tier_changed:"ev-tier",model_changed:"ev-tier",pressure_lifted:"ev-tier",checkpoint_persisted:"ev-checkpoint",recall_inject:"ev-recall",anchors_updated:"ev-recall",config_updated:"ev-config",config_preset:"ev-config",crew_presence_changed:"ev-crew",crew_turn_changed:"ev-crew",crew_bandit_chosen:"ev-crew",session_sample:"ev-recall",game_ritual_start:"ev-game",game_ritual_stage:"ev-game",game_ritual_end:"ev-game",game_mode_changed:"ev-game",game_render:"ev-game"},_=["all","compact_start","compact_end","recall_inject","checkpoint_persisted"];function h(e){switch(e.type){case"compact_start":return`trigger=${e.trigger} session=${e.sessionId.slice(0,8)}`;case"compact_end":return`freed=${e.tokensFreed} ok=${e.success} cp=${e.checkpointId.slice(0,8)}`;case"compact_trigger":return`pressure=${e.pressure}% threshold=${e.threshold}% armed=${e.armed}`;case"compact_skip":return`reason=${e.reason}`;case"tier_changed":return`${e.from} → ${e.to} ctx=${e.contextPct}%`;case"model_changed":return`${e.providerName}/${e.model}`;case"pressure_lifted":return`${e.beforePct}% → ${e.afterPct}%`;case"checkpoint_persisted":return`cp=${e.checkpointId.slice(0,8)} sessionTok=${e.sessionTokens}`;case"recall_inject":return`q="${e.query.slice(0,40)}" chunks=${e.chunks} tok=${e.tokens}`;case"anchors_updated":return`count=${e.count} pinned=${e.pinned}`;case"config_updated":return`key=${e.key}`;case"config_preset":return`preset=${e.preset}`;case"crew_presence_changed":return`agents=${e.activeAgents} turn=${e.currentTurn}`;case"crew_turn_changed":return`turn=${e.turnIndex} agent=${e.agentName}`;case"crew_bandit_chosen":return`agent=${e.chosenAgent} score=${e.score} regret=${e.regret}`;case"session_sample":return`sess=${e.sessionId.slice(0,8)} tok=${e.tokens.toLocaleString()} ctx=${Math.round(e.percent)}%`;case"game_ritual_start":case"game_ritual_stage":case"game_ritual_end":case"game_mode_changed":case"game_render":return`stage=${e.type}`;default:return`event=${e.type??"unknown"}`}}function f(e){try{return new Date(e).toLocaleTimeString()}catch{return e}}function x({events:e}){const[t,n]=o.useState("all"),[a,i]=o.useState(null),l=o.useMemo(()=>(t==="all"?e:e.filter(c=>c.type===t)).slice(-200).reverse(),[e,t]);return r.jsxs("div",{className:"flex flex-col gap-3",children:[r.jsxs("div",{className:"flex flex-wrap items-center gap-2",role:"toolbar",children:[_.map(s=>r.jsx(u,{pressed:t===s,onClick:()=>n(s),children:s==="all"?"all":s.replace(/_/g," ")},s)),r.jsxs("span",{className:"text-xs text-muted-foreground",children:[e.length," buffered"]})]}),r.jsxs("ul",{className:"flex flex-col gap-1 font-mono text-xs",children:[l.length===0&&r.jsx("li",{className:"text-muted-foreground",children:"No events yet."}),l.map((s,c)=>{const d=p[s.type]??"ev-default";return r.jsxs("li",{className:`rounded-md px-2 py-1.5 transition-colors hover:bg-bg-elevated/40 ${d}`,onClick:()=>i(a===c?null:c),children:[r.jsx("span",{className:"ev-time",children:f(s.ts)})," ",r.jsx("span",{className:"ev-type",children:s.type})," ",r.jsx("span",{className:"ev-summary",children:h(s)}),a===c&&r.jsx("pre",{className:"mt-2 overflow-x-auto rounded-md bg-bg-elevated/60 p-2 text-xs",children:JSON.stringify(s,null,2)})]},`${s.ts}-${c}`)})]})]})}const $=["all","compact","recall","config","crew","game"];function w({active:e,onFilter:t}){return r.jsx("div",{className:"flex flex-wrap gap-2",role:"toolbar","aria-label":"Event category filter",children:$.map(n=>r.jsx(u,{pressed:e===n,onClick:()=>t(n),children:n},n))})}const j=new Set(["compact_start","compact_end","compact_trigger","compact_skip"]),k=new Set(["config_updated","config_preset","tier_changed","model_changed","pressure_lifted","anchors_updated","checkpoint_persisted"]),y=new Set(["recall_inject","hyde_executed","recall_metrics"]),S=new Set(["crew_presence_changed","crew_turn_changed","crew_bandit_chosen"]),E=new Set(["game_ritual_start","game_ritual_stage","game_ritual_end","game_mode_changed","game_render"]);function N(e,t){if(t==="all")return!0;const n=e.type;return t==="compact"?j.has(n):t==="recall"?y.has(n):t==="config"?k.has(n):t==="crew"?S.has(n):E.has(n)}function P(){const{events:e,status:t,eventCount:n}=m(),[a,i]=o.useState("all"),l=o.useMemo(()=>e.filter(d=>N(d,a)),[e,a]),s=t==="connected"?"success":t==="error"?"danger":t==="connecting"?"warning":"outline",c=t==="connected"?"connected":t==="connecting"?"connecting…":t==="error"?"error":"disconnected";return r.jsxs("div",{className:"flex flex-col gap-4",children:[r.jsxs("div",{className:"flex items-center justify-between",children:[r.jsxs(g,{variant:s,role:"status",children:[r.jsx("span",{className:"mr-1 inline-block h-1.5 w-1.5 rounded-full bg-current","aria-hidden":"true"}),c]}),r.jsxs("span",{className:"text-xs text-muted-foreground",children:[n," events received"]})]}),r.jsx(w,{active:a,onFilter:i}),r.jsx(x,{events:l})]})}export{P as default};
2
- //# sourceMappingURL=EventsTab-DE29fuqe.js.map
1
+ import{r as o,j as r,B as g}from"./index-C3ZUU5vU.js";import{u as m}from"./useSSE-D-OoWxa2.js";import{T as u}from"./toggle-bJGW8eFJ.js";const p={compact_start:"ev-compact",compact_end:"ev-compact",compact_trigger:"ev-trigger",compact_skip:"ev-trigger",tier_changed:"ev-tier",model_changed:"ev-tier",pressure_lifted:"ev-tier",checkpoint_persisted:"ev-checkpoint",recall_inject:"ev-recall",anchors_updated:"ev-recall",config_updated:"ev-config",config_preset:"ev-config",crew_presence_changed:"ev-crew",crew_turn_changed:"ev-crew",crew_bandit_chosen:"ev-crew",session_sample:"ev-recall",game_ritual_start:"ev-game",game_ritual_stage:"ev-game",game_ritual_end:"ev-game",game_mode_changed:"ev-game",game_render:"ev-game"},_=["all","compact_start","compact_end","recall_inject","checkpoint_persisted"];function h(e){switch(e.type){case"compact_start":return`trigger=${e.trigger} session=${e.sessionId.slice(0,8)}`;case"compact_end":return`freed=${e.tokensFreed} ok=${e.success} cp=${e.checkpointId.slice(0,8)}`;case"compact_trigger":return`pressure=${e.pressure}% threshold=${e.threshold}% armed=${e.armed}`;case"compact_skip":return`reason=${e.reason}`;case"tier_changed":return`${e.from} → ${e.to} ctx=${e.contextPct}%`;case"model_changed":return`${e.providerName}/${e.model}`;case"pressure_lifted":return`${e.beforePct}% → ${e.afterPct}%`;case"checkpoint_persisted":return`cp=${e.checkpointId.slice(0,8)} sessionTok=${e.sessionTokens}`;case"recall_inject":return`q="${e.query.slice(0,40)}" chunks=${e.chunks} tok=${e.tokens}`;case"anchors_updated":return`count=${e.count} pinned=${e.pinned}`;case"config_updated":return`key=${e.key}`;case"config_preset":return`preset=${e.preset}`;case"crew_presence_changed":return`agents=${e.activeAgents} turn=${e.currentTurn}`;case"crew_turn_changed":return`turn=${e.turnIndex} agent=${e.agentName}`;case"crew_bandit_chosen":return`agent=${e.chosenAgent} score=${e.score} regret=${e.regret}`;case"session_sample":return`sess=${e.sessionId.slice(0,8)} tok=${e.tokens.toLocaleString()} ctx=${Math.round(e.percent)}%`;case"game_ritual_start":case"game_ritual_stage":case"game_ritual_end":case"game_mode_changed":case"game_render":return`stage=${e.type}`;default:return`event=${e.type??"unknown"}`}}function f(e){try{return new Date(e).toLocaleTimeString()}catch{return e}}function x({events:e}){const[t,n]=o.useState("all"),[a,i]=o.useState(null),l=o.useMemo(()=>(t==="all"?e:e.filter(c=>c.type===t)).slice(-200).reverse(),[e,t]);return r.jsxs("div",{className:"flex flex-col gap-3",children:[r.jsxs("div",{className:"flex flex-wrap items-center gap-2",role:"toolbar",children:[_.map(s=>r.jsx(u,{pressed:t===s,onClick:()=>n(s),children:s==="all"?"all":s.replace(/_/g," ")},s)),r.jsxs("span",{className:"text-xs text-muted-foreground",children:[e.length," buffered"]})]}),r.jsxs("ul",{className:"flex flex-col gap-1 font-mono text-xs",children:[l.length===0&&r.jsx("li",{className:"text-muted-foreground",children:"No events yet."}),l.map((s,c)=>{const d=p[s.type]??"ev-default";return r.jsxs("li",{className:`rounded-md px-2 py-1.5 transition-colors hover:bg-bg-elevated/40 ${d}`,onClick:()=>i(a===c?null:c),children:[r.jsx("span",{className:"ev-time",children:f(s.ts)})," ",r.jsx("span",{className:"ev-type",children:s.type})," ",r.jsx("span",{className:"ev-summary",children:h(s)}),a===c&&r.jsx("pre",{className:"mt-2 overflow-x-auto rounded-md bg-bg-elevated/60 p-2 text-xs",children:JSON.stringify(s,null,2)})]},`${s.ts}-${c}`)})]})]})}const $=["all","compact","recall","config","crew","game"];function w({active:e,onFilter:t}){return r.jsx("div",{className:"flex flex-wrap gap-2",role:"toolbar","aria-label":"Event category filter",children:$.map(n=>r.jsx(u,{pressed:e===n,onClick:()=>t(n),children:n},n))})}const j=new Set(["compact_start","compact_end","compact_trigger","compact_skip"]),k=new Set(["config_updated","config_preset","tier_changed","model_changed","pressure_lifted","anchors_updated","checkpoint_persisted"]),y=new Set(["recall_inject","hyde_executed","recall_metrics"]),S=new Set(["crew_presence_changed","crew_turn_changed","crew_bandit_chosen"]),E=new Set(["game_ritual_start","game_ritual_stage","game_ritual_end","game_mode_changed","game_render"]);function N(e,t){if(t==="all")return!0;const n=e.type;return t==="compact"?j.has(n):t==="recall"?y.has(n):t==="config"?k.has(n):t==="crew"?S.has(n):E.has(n)}function P(){const{events:e,status:t,eventCount:n}=m(),[a,i]=o.useState("all"),l=o.useMemo(()=>e.filter(d=>N(d,a)),[e,a]),s=t==="connected"?"success":t==="error"?"danger":t==="connecting"?"warning":"outline",c=t==="connected"?"connected":t==="connecting"?"connecting…":t==="error"?"error":"disconnected";return r.jsxs("div",{className:"flex flex-col gap-4",children:[r.jsxs("div",{className:"flex items-center justify-between",children:[r.jsxs(g,{variant:s,role:"status",children:[r.jsx("span",{className:"mr-1 inline-block h-1.5 w-1.5 rounded-full bg-current","aria-hidden":"true"}),c]}),r.jsxs("span",{className:"text-xs text-muted-foreground",children:[n," events received"]})]}),r.jsx(w,{active:a,onFilter:i}),r.jsx(x,{events:l})]})}export{P as default};
2
+ //# sourceMappingURL=EventsTab-8Y7if14O.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"EventsTab-DE29fuqe.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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"}
1
+ {"version":3,"file":"EventsTab-8Y7if14O.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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"}
@@ -1,2 +1,2 @@
1
- import{r as d,j as e,B as g}from"./index-iuxgtzEW.js";import{fetchContextHealth as N}from"./health-lSeDFTz7.js";import{C as i,c as h,a as u,b as j}from"./card-Cfhz9X31.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-D4eVI8ar.js.map
1
+ import{r as d,j as e,B as g}from"./index-C3ZUU5vU.js";import{fetchContextHealth as N}from"./health-lSeDFTz7.js";import{C as i,c as h,a as u,b as j}from"./card-COtt-Q5y.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-Cw5eYIYq.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"HealthTab-D4eVI8ar.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-Cw5eYIYq.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 n,_ as E,$ as R,a0 as C,u as w,a1 as D,a2 as A}from"./index-iuxgtzEW.js";import{C as p,a as j,b as f,c as y}from"./card-Cfhz9X31.js";import{B as g}from"./button-DhQQAJGm.js";import{S as B}from"./switch-7EHEQMSP.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]=n.useState([]),[s,i]=n.useState(null),[d,m]=n.useState("30"),u=n.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(a=>[{...c,timestamp:Date.now()},...a].slice(0,20))}catch(c){l(a=>[{operation:r.key,success:!1,affected:0,reclaimedBytes:0,summary:c instanceof Error?c.message:String(c),timestamp:Date.now()},...a].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(a=>a.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:a=>m(a.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]=n.useState(null),[s,i]=n.useState(!1),[d,m]=n.useState(null),[u,r]=n.useState(!1),h=n.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=n.useCallback(()=>{if(!t)return;const o=JSON.stringify(t,null,2);navigator.clipboard.writeText(o).catch(()=>{})},[t]),a=n.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:a,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]=n.useState(!1),[s,i]=n.useState(!0),[d,m]=n.useState(!1),[u,r]=n.useState(null),h=n.useCallback(async()=>{try{const{fetchHealthSettings:a}=await C(async()=>{const{fetchHealthSettings:o}=await import("./health-lSeDFTz7.js");return{fetchHealthSettings:o}},[],import.meta.url),x=await a();l(x.mitigate)}catch(a){r(a instanceof Error?a.message:String(a))}finally{i(!1)}},[]);n.useEffect(()=>{h()},[h]);const c=n.useCallback(async()=>{m(!0),r(null);try{const{setHealthMitigate:a}=await C(async()=>{const{setHealthMitigate:o}=await import("./health-lSeDFTz7.js");return{setHealthMitigate:o}},[],import.meta.url),x=!t;await a(x),l(x)}catch(a){r(a instanceof Error?a.message:String(a))}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(n.useCallback(()=>D(),[]),{pollInterval:15e3}),{data:i,loading:d,error:m}=w(n.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-4fJ7M4S4.js.map
1
+ import{j as e,B as k,r as n,_ as E,$ as R,a0 as C,u as w,a1 as D,a2 as A}from"./index-C3ZUU5vU.js";import{C as p,a as j,b as f,c as y}from"./card-COtt-Q5y.js";import{B as g}from"./button-B38fJDBG.js";import{S as B}from"./switch-DvYCCZ0E.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]=n.useState([]),[s,i]=n.useState(null),[d,m]=n.useState("30"),u=n.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(a=>[{...c,timestamp:Date.now()},...a].slice(0,20))}catch(c){l(a=>[{operation:r.key,success:!1,affected:0,reclaimedBytes:0,summary:c instanceof Error?c.message:String(c),timestamp:Date.now()},...a].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(a=>a.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:a=>m(a.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]=n.useState(null),[s,i]=n.useState(!1),[d,m]=n.useState(null),[u,r]=n.useState(!1),h=n.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=n.useCallback(()=>{if(!t)return;const o=JSON.stringify(t,null,2);navigator.clipboard.writeText(o).catch(()=>{})},[t]),a=n.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:a,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]=n.useState(!1),[s,i]=n.useState(!0),[d,m]=n.useState(!1),[u,r]=n.useState(null),h=n.useCallback(async()=>{try{const{fetchHealthSettings:a}=await C(async()=>{const{fetchHealthSettings:o}=await import("./health-lSeDFTz7.js");return{fetchHealthSettings:o}},[],import.meta.url),x=await a();l(x.mitigate)}catch(a){r(a instanceof Error?a.message:String(a))}finally{i(!1)}},[]);n.useEffect(()=>{h()},[h]);const c=n.useCallback(async()=>{m(!0),r(null);try{const{setHealthMitigate:a}=await C(async()=>{const{setHealthMitigate:o}=await import("./health-lSeDFTz7.js");return{setHealthMitigate:o}},[],import.meta.url),x=!t;await a(x),l(x)}catch(a){r(a instanceof Error?a.message:String(a))}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(n.useCallback(()=>D(),[]),{pollInterval:15e3}),{data:i,loading:d,error:m}=w(n.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-B0o3a57B.js.map