pi-mega-compact 0.11.2 → 0.11.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/dist/extensions/dashboard-server/api-contracts/endpoints.js +9 -1
  2. package/dist/extensions/dashboard-server/api-contracts/provider-cache.js +7 -0
  3. package/dist/extensions/dashboard-server/api-contracts.test.js +3 -2
  4. package/dist/extensions/dashboard-server/index-reader.js +22 -0
  5. package/dist/extensions/dashboard-server/routes-cache.js +80 -0
  6. package/dist/extensions/dashboard-server/routes-cache.test.js +248 -0
  7. package/dist/extensions/dashboard-server/routes-maintenance.js +7 -7
  8. package/dist/extensions/dashboard-server/routes-repo.js +24 -0
  9. package/dist/extensions/dashboard-server/routes.js +1 -0
  10. package/dist/extensions/dashboard-server/server.js +3 -1
  11. package/dist/extensions/mega-cache-replay.test.js +233 -24
  12. package/dist/extensions/mega-compact-s38.test.js +201 -6
  13. package/dist/extensions/mega-config.js +3 -0
  14. package/dist/extensions/mega-events/agent-handlers.js +23 -7
  15. package/dist/extensions/mega-events/context-handler.js +37 -19
  16. package/dist/extensions/mega-events/error-classifier.js +27 -16
  17. package/dist/extensions/mega-runtime/runtime-snapshot.js +14 -4
  18. package/dist/extensions/mega-runtime/snapshot.js +1 -1
  19. package/dist/extensions/mega-runtime/snapshot.test.js +127 -0
  20. package/dist/extensions/mega-runtime/widget.js +4 -4
  21. package/dist/extensions/mega-runtime/widget.test.js +103 -12
  22. package/dist/src/pricing.js +78 -0
  23. package/dist/src/store/sqlite/perf-samples.js +104 -0
  24. package/dist/src/store/sqlite/perf-samples.test.js +93 -1
  25. package/extensions/dashboard-client/dist/assets/{AchievementTiles-Cml6YC8w.js → AchievementTiles-vEo2MtE2.js} +2 -2
  26. package/extensions/dashboard-client/dist/assets/{AchievementTiles-Cml6YC8w.js.map → AchievementTiles-vEo2MtE2.js.map} +1 -1
  27. package/extensions/dashboard-client/dist/assets/AchievementsTab-DjxfYrDc.js +2 -0
  28. package/extensions/dashboard-client/dist/assets/{AchievementsTab-B8NqDt7I.js.map → AchievementsTab-DjxfYrDc.js.map} +1 -1
  29. package/extensions/dashboard-client/dist/assets/CacheTab-Dnpv94wq.js +2 -0
  30. package/extensions/dashboard-client/dist/assets/CacheTab-Dnpv94wq.js.map +1 -0
  31. package/extensions/dashboard-client/dist/assets/{ConfigTab-CBYvp2mV.js → ConfigTab-Cm6ymbmx.js} +2 -2
  32. package/extensions/dashboard-client/dist/assets/{ConfigTab-CBYvp2mV.js.map → ConfigTab-Cm6ymbmx.js.map} +1 -1
  33. package/extensions/dashboard-client/dist/assets/{EventsTab-DlTJqiDm.js → EventsTab-DXWl_YzI.js} +2 -2
  34. package/extensions/dashboard-client/dist/assets/{EventsTab-DlTJqiDm.js.map → EventsTab-DXWl_YzI.js.map} +1 -1
  35. package/extensions/dashboard-client/dist/assets/{GameTab-DkGTqKJW.js → GameTab-BLDudcO5.js} +2 -2
  36. package/extensions/dashboard-client/dist/assets/{GameTab-DkGTqKJW.js.map → GameTab-BLDudcO5.js.map} +1 -1
  37. package/extensions/dashboard-client/dist/assets/MaintenanceTab-DLkKCztn.js +2 -0
  38. package/extensions/dashboard-client/dist/assets/MaintenanceTab-DLkKCztn.js.map +1 -0
  39. package/extensions/dashboard-client/dist/assets/{MetricsTab-BFo7pKM-.js → MetricsTab-Cq4u-rZF.js} +2 -2
  40. package/extensions/dashboard-client/dist/assets/{MetricsTab-BFo7pKM-.js.map → MetricsTab-Cq4u-rZF.js.map} +1 -1
  41. package/extensions/dashboard-client/dist/assets/{OverviewTab-CClCUd3-.js → OverviewTab-D4kGvsiQ.js} +2 -2
  42. package/extensions/dashboard-client/dist/assets/{OverviewTab-CClCUd3-.js.map → OverviewTab-D4kGvsiQ.js.map} +1 -1
  43. package/extensions/dashboard-client/dist/assets/ReposTab-C0Wnrf2u.js +2 -0
  44. package/extensions/dashboard-client/dist/assets/ReposTab-C0Wnrf2u.js.map +1 -0
  45. package/extensions/dashboard-client/dist/assets/{SessionsTab-CCYd-kSz.js → SessionsTab-DZC2-GCn.js} +2 -2
  46. package/extensions/dashboard-client/dist/assets/{SessionsTab-CCYd-kSz.js.map → SessionsTab-DZC2-GCn.js.map} +1 -1
  47. package/extensions/dashboard-client/dist/assets/TimeSavedCard-UqljSyP-.js +2 -0
  48. package/extensions/dashboard-client/dist/assets/TimeSavedCard-UqljSyP-.js.map +1 -0
  49. package/extensions/dashboard-client/dist/assets/{TopicsTab-DRLJmVv0.js → TopicsTab-01G71hv1.js} +2 -2
  50. package/extensions/dashboard-client/dist/assets/{TopicsTab-DRLJmVv0.js.map → TopicsTab-01G71hv1.js.map} +1 -1
  51. package/extensions/dashboard-client/dist/assets/{TurnsTab-CLNM0T5R.js → TurnsTab-DLRg0LZT.js} +2 -2
  52. package/extensions/dashboard-client/dist/assets/{TurnsTab-CLNM0T5R.js.map → TurnsTab-DLRg0LZT.js.map} +1 -1
  53. package/extensions/dashboard-client/dist/assets/index-BEdKlpAB.css +1 -0
  54. package/extensions/dashboard-client/dist/assets/{index-BcoSz-L4.js → index-BX0bcgLu.js} +4 -4
  55. package/extensions/dashboard-client/dist/assets/{index-BcoSz-L4.js.map → index-BX0bcgLu.js.map} +1 -1
  56. package/extensions/dashboard-client/dist/assets/{useSSE-Cezlih5V.js → useSSE-BBYt5UPB.js} +2 -2
  57. package/extensions/dashboard-client/dist/assets/{useSSE-Cezlih5V.js.map → useSSE-BBYt5UPB.js.map} +1 -1
  58. package/extensions/dashboard-client/dist/index.html +2 -2
  59. package/extensions/dashboard-client/src/api/client.ts +6 -0
  60. package/extensions/dashboard-client/src/components/ActiveReposTable.tsx +13 -1
  61. package/extensions/dashboard-client/src/components/ProviderCacheCard.tsx +141 -0
  62. package/extensions/dashboard-client/src/components/SavingsByModelTable.tsx +31 -3
  63. package/extensions/dashboard-client/src/main.tsx +1 -0
  64. package/extensions/dashboard-client/src/styles/maintenance.css +267 -0
  65. package/extensions/dashboard-client/src/styles/overview-extra.css +9 -0
  66. package/extensions/dashboard-client/src/tabs/CacheTab.tsx +83 -29
  67. package/extensions/dashboard-client/src/tabs/MaintenanceTab.tsx +301 -136
  68. package/extensions/dashboard-client/src/utils/format.ts +42 -7
  69. package/extensions/dashboard-server/api-contracts/endpoints.ts +31 -8
  70. package/extensions/dashboard-server/api-contracts/index.ts +4 -0
  71. package/extensions/dashboard-server/api-contracts/multi-repo.ts +6 -0
  72. package/extensions/dashboard-server/api-contracts/provider-cache.ts +30 -0
  73. package/extensions/dashboard-server/api-contracts.test.ts +3 -2
  74. package/extensions/dashboard-server/index-reader.ts +23 -0
  75. package/extensions/dashboard-server/routes-cache.test.ts +302 -0
  76. package/extensions/dashboard-server/routes-cache.ts +123 -0
  77. package/extensions/dashboard-server/routes-maintenance.ts +18 -11
  78. package/extensions/dashboard-server/routes-repo.ts +31 -4
  79. package/extensions/dashboard-server/routes.ts +1 -0
  80. package/extensions/dashboard-server/server.ts +2 -0
  81. package/extensions/mega-cache-replay.test.ts +481 -166
  82. package/extensions/mega-compact-s38.test.ts +210 -6
  83. package/extensions/mega-config.ts +6 -0
  84. package/extensions/mega-events/agent-handlers.ts +24 -6
  85. package/extensions/mega-events/context-handler.ts +59 -24
  86. package/extensions/mega-events/error-classifier.ts +30 -13
  87. package/extensions/mega-runtime/runtime-snapshot.ts +31 -9
  88. package/extensions/mega-runtime/snapshot.test.ts +139 -0
  89. package/extensions/mega-runtime/snapshot.ts +6 -4
  90. package/extensions/mega-runtime/widget.test.ts +290 -105
  91. package/extensions/mega-runtime/widget.ts +29 -6
  92. package/package.json +1 -1
  93. package/src/pricing.ts +95 -0
  94. package/src/store/sqlite/perf-samples.test.ts +107 -3
  95. package/src/store/sqlite/perf-samples.ts +142 -6
  96. package/extensions/dashboard-client/dist/assets/AchievementsTab-B8NqDt7I.js +0 -2
  97. package/extensions/dashboard-client/dist/assets/CacheTab-BCLzVL4S.js +0 -2
  98. package/extensions/dashboard-client/dist/assets/CacheTab-BCLzVL4S.js.map +0 -1
  99. package/extensions/dashboard-client/dist/assets/MaintenanceTab-Cw43zmUx.js +0 -2
  100. package/extensions/dashboard-client/dist/assets/MaintenanceTab-Cw43zmUx.js.map +0 -1
  101. package/extensions/dashboard-client/dist/assets/ReposTab-_3R4RSLZ.js +0 -2
  102. package/extensions/dashboard-client/dist/assets/ReposTab-_3R4RSLZ.js.map +0 -1
  103. package/extensions/dashboard-client/dist/assets/TimeSavedCard-0TqsrY6y.js +0 -2
  104. package/extensions/dashboard-client/dist/assets/TimeSavedCard-0TqsrY6y.js.map +0 -1
  105. package/extensions/dashboard-client/dist/assets/index-gn66lYD2.css +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"GameTab-DkGTqKJW.js","sources":["../../src/tabs/GameTab.tsx"],"sourcesContent":["/**\n * dashboard-client/src/tabs/GameTab.tsx — Game tab (NEW).\n *\n * Renders:\n * (1) MEGA CACHE banner (animated gradient, shown when any mega_cache > 100)\n * (2) Opie unlock tile (shown when mega_cache > 100)\n * (3) High Scores header\n * (4) 4 leaderboards in 2×2 grid: Cache %, Dedupe, Turns (LVL badge),\n * MEGA CACHE trophies (firstSeen date meta)\n * (5) Repos count badge via fetchGameScores({metric:'repos',limit:1})\n * (6) Achievements sub-section (AchievementTiles shared component)\n * (7) Game empty state\n * Polls scores + achievements every 15s, game-state every 15s.\n * Applies data-theme accent vars when game_mode_on.\n */\n\nimport type React from \"react\";\nimport { useState, useEffect, useCallback, useRef } from \"react\";\nimport type { GameScoreRow, AchievementRow } from \"@contracts\";\nimport { fetchGameScores, fetchAchievements, fetchGameState } from \"../api/client\";\nimport { AchievementTiles } from \"../components/AchievementTiles\";\n\n/** Actual flat game state shape from the server (NOT the nested contract). */\ninterface FlatGameState {\n\tgame_mode_on: boolean;\n\ttheme: string;\n\ttui_display_mode: \"full\" | \"minimal\";\n}\n\n/** Metadata object inside a mega_cache GameScoreRow. */\ninterface TrophyMeta {\n\tfirstSeenTs?: number;\n\tfirstSeen?: number;\n}\n\n/* ── Format helpers (matching html.ts logic) ─────────────────────────── */\n\nfunction fmtPct(v: number): string {\n\treturn (Math.round(v * 10) / 10).toString() + \"%\";\n}\n\nfunction fmtDate(ts: number | null | undefined): string {\n\treturn ts != null ? new Date(ts).toLocaleString() : \"\\u2014\";\n}\n\nfunction trophyMeta(m: unknown): TrophyMeta {\n\treturn m && typeof m === \"object\" ? (m as TrophyMeta) : {};\n}\n\nfunction repoBasename(path: string): string {\n\treturn path.split(\"/\").pop() || path;\n}\n\n/* ── Leaderboard card ────────────────────────────────────────────────── */\n\ninterface LeaderboardCardProps {\n\ttitle: string;\n\trows: GameScoreRow[];\n\tbadge?: React.ReactNode;\n\trenderValue: (r: GameScoreRow) => React.ReactNode;\n}\n\nfunction LeaderboardCard({\n\ttitle,\n\trows,\n\tbadge,\n\trenderValue,\n}: LeaderboardCardProps): React.ReactElement {\n\treturn (\n\t\t<div className=\"lb-card\">\n\t\t\t<h3>\n\t\t\t\t{title}\n\t\t\t\t{badge}\n\t\t\t</h3>\n\t\t\t{rows.length === 0 ? (\n\t\t\t\t<div className=\"lb-empty\">—</div>\n\t\t\t) : (\n\t\t\t\t<table>\n\t\t\t\t\t<tbody>\n\t\t\t\t\t\t{rows.map((r, i) => (\n\t\t\t\t\t\t\t<tr key={`${r.repo_root}-${i}`}>\n\t\t\t\t\t\t\t\t<td title={r.repo_root}>{repoBasename(r.repo_root)}</td>\n\t\t\t\t\t\t\t\t<td className=\"num\">{renderValue(r)}</td>\n\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</tbody>\n\t\t\t\t</table>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n\n/* ── Main GameTab component ──────────────────────────────────────────── */\n\nexport default function GameTab(): React.ReactElement {\n\tconst [scores, setScores] = useState<\n\t\tRecord<string, GameScoreRow[]>\n\t>({});\n\tconst [reposCount, setReposCount] = useState(0);\n\tconst [achievements, setAchievements] = useState<AchievementRow[]>([]);\n\tconst [gameModeOn, setGameModeOn] = useState(false);\n\tconst [loading, setLoading] = useState(true);\n\tconst [megaToast, setMegaToast] = useState<string | null>(null);\n\tconst lastMegaTsRef = useRef(0);\n\n\tconst fetchAll = useCallback(async (): Promise<void> => {\n\t\ttry {\n\t\t\tconst [cache, dedupe, turns, megaCache, repos, ach] =\n\t\t\t\tawait Promise.all([\n\t\t\t\t\tfetchGameScores({ metric: \"cache\", limit: 25 }),\n\t\t\t\t\tfetchGameScores({ metric: \"dedupe\", limit: 25 }),\n\t\t\t\t\tfetchGameScores({ metric: \"turns\", limit: 25 }),\n\t\t\t\t\tfetchGameScores({ metric: \"mega_cache\", limit: 25 }),\n\t\t\t\t\tfetchGameScores({ metric: \"repos\", limit: 1 }),\n\t\t\t\t\tfetchAchievements(),\n\t\t\t\t]);\n\n\t\t\t/* Mega-cache toast: new row with value > 100 since last poll */\n\t\t\tconst newRow = megaCache.find(\n\t\t\t\t(r) => r.ts > lastMegaTsRef.current && r.value > 100,\n\t\t\t);\n\t\t\tif (lastMegaTsRef.current > 0 && newRow) {\n\t\t\t\tsetMegaToast(\n\t\t\t\t\t`oopsie! cache went to ${Math.round(newRow.value)}% — MEGA CACHE \\uD83E\\uDD67`,\n\t\t\t\t);\n\t\t\t\tsetTimeout(() => setMegaToast(null), 4000);\n\t\t\t}\n\t\t\tif (megaCache.length > 0) {\n\t\t\t\tlastMegaTsRef.current = Math.max(\n\t\t\t\t\t...megaCache.map((r) => r.ts),\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tsetScores({ cache, dedupe, turns, mega_cache: megaCache });\n\t\t\tsetReposCount(repos[0]?.value ?? 0);\n\t\t\tsetAchievements(ach);\n\t\t} catch {\n\t\t\t/* non-fatal */\n\t\t} finally {\n\t\t\tsetLoading(false);\n\t\t}\n\t}, []);\n\n\t/* Poll scores + achievements every 15s. */\n\tuseEffect(() => {\n\t\tlet active = true;\n\t\tconst doFetch = async (): Promise<void> => {\n\t\t\tif (active) await fetchAll();\n\t\t};\n\t\tvoid doFetch();\n\t\tconst timer = setInterval(() => void doFetch(), 15000);\n\t\treturn () => {\n\t\t\tactive = false;\n\t\t\tclearInterval(timer);\n\t\t};\n\t}, [fetchAll]);\n\n\t/* Poll game state every 15s for styling. */\n\tuseEffect(() => {\n\t\tlet active = true;\n\t\tconst doFetch = async (): Promise<void> => {\n\t\t\ttry {\n\t\t\t\tconst gs = (await fetchGameState()) as unknown as FlatGameState;\n\t\t\t\tif (active) {\n\t\t\t\t\tsetGameModeOn(!!gs.game_mode_on);\n\t\t\t\t\tif (gs.theme)\n\t\t\t\t\t\tdocument.documentElement.dataset.theme = gs.theme;\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t/* non-fatal */\n\t\t\t}\n\t\t};\n\t\tvoid doFetch();\n\t\tconst timer = setInterval(() => void doFetch(), 15000);\n\t\treturn () => {\n\t\t\tactive = false;\n\t\t\tclearInterval(timer);\n\t\t};\n\t}, []);\n\n\t/* Compute mega-cache banner data */\n\tconst megaRows = scores.mega_cache ?? [];\n\tlet bestMega: number | null = null;\n\tlet firstSeen: number | null = null;\n\tfor (const r of megaRows) {\n\t\tif (bestMega == null || r.value > bestMega) bestMega = r.value;\n\t\tconst m = trophyMeta(r.meta);\n\t\tconst fs = m.firstSeenTs ?? m.firstSeen ?? r.ts;\n\t\tif (firstSeen == null || fs < firstSeen) firstSeen = fs;\n\t}\n\tconst showMega = bestMega != null && bestMega > 100;\n\n\t/* Turns level badge */\n\tconst turnsRows = scores.turns ?? [];\n\tconst maxTurns = turnsRows.reduce((mx, r) => Math.max(mx, r.value), 0);\n\tconst lvl = Math.floor(Math.log2(maxTurns + 1)) + 1;\n\n\t/* Game empty state */\n\tconst hasData =\n\t\t(scores.cache?.length ?? 0) > 0 ||\n\t\t(scores.dedupe?.length ?? 0) > 0 ||\n\t\tturnsRows.length > 0 ||\n\t\tmegaRows.length > 0 ||\n\t\treposCount > 0;\n\n\treturn (\n\t\t<div className={`game-tab${gameModeOn ? \" game-mode-on\" : \"\"}`}>\n\t\t\t{megaToast != null && (\n\t\t\t\t<div className=\"mega-cache-toast show\">{megaToast}</div>\n\t\t\t)}\n\n\t\t\t{showMega && (\n\t\t\t\t<div className=\"mega-cache-banner\">\n\t\t\t\t\t{\"\\uD83E\\uDD67\"} MEGA CACHE! peak {fmtPct(bestMega!)} — first\n\t\t\t\t\treached {fmtDate(firstSeen)}\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{showMega && (\n\t\t\t\t<div className=\"achievement-tile unlocked\">\n\t\t\t\t\t{\"\\uD83C\\uDFC6\"} Opie's Wild Ride\n\t\t\t\t\t<span className=\"ach-detail\">\n\t\t\t\t\t\tbest {fmtPct(bestMega!)} · first {fmtDate(firstSeen)}\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t<h2>High Scores</h2>\n\n\t\t\t{!hasData && !loading && (\n\t\t\t\t<div className=\"game-empty\">\n\t\t\t\t\tNo scores yet — run a session with game mode on.\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{hasData && (\n\t\t\t\t<div className=\"game-leaderboards\">\n\t\t\t\t\t<LeaderboardCard\n\t\t\t\t\t\ttitle=\"Cache %\"\n\t\t\t\t\t\trows={scores.cache ?? []}\n\t\t\t\t\t\trenderValue={(r) => fmtPct(r.value)}\n\t\t\t\t\t/>\n\t\t\t\t\t<LeaderboardCard\n\t\t\t\t\t\ttitle=\"Dedupe (collapsed)\"\n\t\t\t\t\t\trows={scores.dedupe ?? []}\n\t\t\t\t\t\trenderValue={(r) => r.value.toLocaleString()}\n\t\t\t\t\t/>\n\t\t\t\t\t<LeaderboardCard\n\t\t\t\t\t\ttitle=\"Turns\"\n\t\t\t\t\t\trows={turnsRows}\n\t\t\t\t\t\tbadge={\n\t\t\t\t\t\t\tturnsRows.length > 0 ? (\n\t\t\t\t\t\t\t\t<span className=\"lvl-badge\">LVL {lvl}</span>\n\t\t\t\t\t\t\t) : undefined\n\t\t\t\t\t\t}\n\t\t\t\t\t\trenderValue={(r) => r.value.toLocaleString()}\n\t\t\t\t\t/>\n\t\t\t\t\t<LeaderboardCard\n\t\t\t\t\t\ttitle=\"MEGA CACHE trophies\"\n\t\t\t\t\t\trows={megaRows}\n\t\t\t\t\t\trenderValue={(r) => (\n\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t{fmtPct(r.value)}\n\t\t\t\t\t\t\t\t<span className=\"lb-meta\">\n\t\t\t\t\t\t\t\t\t{fmtDate(\n\t\t\t\t\t\t\t\t\t\ttrophyMeta(r.meta).firstSeenTs ??\n\t\t\t\t\t\t\t\t\t\t\ttrophyMeta(r.meta).firstSeen ??\n\t\t\t\t\t\t\t\t\t\t\tr.ts,\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t)}\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{reposCount > 0 && (\n\t\t\t\t<div className=\"repos-badge-line\">\n\t\t\t\t\t<span className=\"repos-badge\">{reposCount} repos</span>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t<h3>Achievements</h3>\n\t\t\t<AchievementTiles achievements={achievements} />\n\t\t</div>\n\t);\n}\n"],"names":["fmtPct","v","fmtDate","ts","trophyMeta","m","repoBasename","path","LeaderboardCard","title","rows","badge","renderValue","jsxs","jsx","r","i","GameTab","scores","setScores","useState","reposCount","setReposCount","achievements","setAchievements","gameModeOn","setGameModeOn","loading","setLoading","megaToast","setMegaToast","lastMegaTsRef","useRef","fetchAll","useCallback","cache","dedupe","turns","megaCache","repos","ach","fetchGameScores","fetchAchievements","newRow","useEffect","active","doFetch","timer","gs","fetchGameState","megaRows","bestMega","firstSeen","fs","showMega","turnsRows","maxTurns","mx","lvl","hasData","Fragment","AchievementTiles"],"mappings":"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"}
1
+ {"version":3,"file":"GameTab-BLDudcO5.js","sources":["../../src/tabs/GameTab.tsx"],"sourcesContent":["/**\n * dashboard-client/src/tabs/GameTab.tsx — Game tab (NEW).\n *\n * Renders:\n * (1) MEGA CACHE banner (animated gradient, shown when any mega_cache > 100)\n * (2) Opie unlock tile (shown when mega_cache > 100)\n * (3) High Scores header\n * (4) 4 leaderboards in 2×2 grid: Cache %, Dedupe, Turns (LVL badge),\n * MEGA CACHE trophies (firstSeen date meta)\n * (5) Repos count badge via fetchGameScores({metric:'repos',limit:1})\n * (6) Achievements sub-section (AchievementTiles shared component)\n * (7) Game empty state\n * Polls scores + achievements every 15s, game-state every 15s.\n * Applies data-theme accent vars when game_mode_on.\n */\n\nimport type React from \"react\";\nimport { useState, useEffect, useCallback, useRef } from \"react\";\nimport type { GameScoreRow, AchievementRow } from \"@contracts\";\nimport { fetchGameScores, fetchAchievements, fetchGameState } from \"../api/client\";\nimport { AchievementTiles } from \"../components/AchievementTiles\";\n\n/** Actual flat game state shape from the server (NOT the nested contract). */\ninterface FlatGameState {\n\tgame_mode_on: boolean;\n\ttheme: string;\n\ttui_display_mode: \"full\" | \"minimal\";\n}\n\n/** Metadata object inside a mega_cache GameScoreRow. */\ninterface TrophyMeta {\n\tfirstSeenTs?: number;\n\tfirstSeen?: number;\n}\n\n/* ── Format helpers (matching html.ts logic) ─────────────────────────── */\n\nfunction fmtPct(v: number): string {\n\treturn (Math.round(v * 10) / 10).toString() + \"%\";\n}\n\nfunction fmtDate(ts: number | null | undefined): string {\n\treturn ts != null ? new Date(ts).toLocaleString() : \"\\u2014\";\n}\n\nfunction trophyMeta(m: unknown): TrophyMeta {\n\treturn m && typeof m === \"object\" ? (m as TrophyMeta) : {};\n}\n\nfunction repoBasename(path: string): string {\n\treturn path.split(\"/\").pop() || path;\n}\n\n/* ── Leaderboard card ────────────────────────────────────────────────── */\n\ninterface LeaderboardCardProps {\n\ttitle: string;\n\trows: GameScoreRow[];\n\tbadge?: React.ReactNode;\n\trenderValue: (r: GameScoreRow) => React.ReactNode;\n}\n\nfunction LeaderboardCard({\n\ttitle,\n\trows,\n\tbadge,\n\trenderValue,\n}: LeaderboardCardProps): React.ReactElement {\n\treturn (\n\t\t<div className=\"lb-card\">\n\t\t\t<h3>\n\t\t\t\t{title}\n\t\t\t\t{badge}\n\t\t\t</h3>\n\t\t\t{rows.length === 0 ? (\n\t\t\t\t<div className=\"lb-empty\">—</div>\n\t\t\t) : (\n\t\t\t\t<table>\n\t\t\t\t\t<tbody>\n\t\t\t\t\t\t{rows.map((r, i) => (\n\t\t\t\t\t\t\t<tr key={`${r.repo_root}-${i}`}>\n\t\t\t\t\t\t\t\t<td title={r.repo_root}>{repoBasename(r.repo_root)}</td>\n\t\t\t\t\t\t\t\t<td className=\"num\">{renderValue(r)}</td>\n\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</tbody>\n\t\t\t\t</table>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n\n/* ── Main GameTab component ──────────────────────────────────────────── */\n\nexport default function GameTab(): React.ReactElement {\n\tconst [scores, setScores] = useState<\n\t\tRecord<string, GameScoreRow[]>\n\t>({});\n\tconst [reposCount, setReposCount] = useState(0);\n\tconst [achievements, setAchievements] = useState<AchievementRow[]>([]);\n\tconst [gameModeOn, setGameModeOn] = useState(false);\n\tconst [loading, setLoading] = useState(true);\n\tconst [megaToast, setMegaToast] = useState<string | null>(null);\n\tconst lastMegaTsRef = useRef(0);\n\n\tconst fetchAll = useCallback(async (): Promise<void> => {\n\t\ttry {\n\t\t\tconst [cache, dedupe, turns, megaCache, repos, ach] =\n\t\t\t\tawait Promise.all([\n\t\t\t\t\tfetchGameScores({ metric: \"cache\", limit: 25 }),\n\t\t\t\t\tfetchGameScores({ metric: \"dedupe\", limit: 25 }),\n\t\t\t\t\tfetchGameScores({ metric: \"turns\", limit: 25 }),\n\t\t\t\t\tfetchGameScores({ metric: \"mega_cache\", limit: 25 }),\n\t\t\t\t\tfetchGameScores({ metric: \"repos\", limit: 1 }),\n\t\t\t\t\tfetchAchievements(),\n\t\t\t\t]);\n\n\t\t\t/* Mega-cache toast: new row with value > 100 since last poll */\n\t\t\tconst newRow = megaCache.find(\n\t\t\t\t(r) => r.ts > lastMegaTsRef.current && r.value > 100,\n\t\t\t);\n\t\t\tif (lastMegaTsRef.current > 0 && newRow) {\n\t\t\t\tsetMegaToast(\n\t\t\t\t\t`oopsie! cache went to ${Math.round(newRow.value)}% — MEGA CACHE \\uD83E\\uDD67`,\n\t\t\t\t);\n\t\t\t\tsetTimeout(() => setMegaToast(null), 4000);\n\t\t\t}\n\t\t\tif (megaCache.length > 0) {\n\t\t\t\tlastMegaTsRef.current = Math.max(\n\t\t\t\t\t...megaCache.map((r) => r.ts),\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tsetScores({ cache, dedupe, turns, mega_cache: megaCache });\n\t\t\tsetReposCount(repos[0]?.value ?? 0);\n\t\t\tsetAchievements(ach);\n\t\t} catch {\n\t\t\t/* non-fatal */\n\t\t} finally {\n\t\t\tsetLoading(false);\n\t\t}\n\t}, []);\n\n\t/* Poll scores + achievements every 15s. */\n\tuseEffect(() => {\n\t\tlet active = true;\n\t\tconst doFetch = async (): Promise<void> => {\n\t\t\tif (active) await fetchAll();\n\t\t};\n\t\tvoid doFetch();\n\t\tconst timer = setInterval(() => void doFetch(), 15000);\n\t\treturn () => {\n\t\t\tactive = false;\n\t\t\tclearInterval(timer);\n\t\t};\n\t}, [fetchAll]);\n\n\t/* Poll game state every 15s for styling. */\n\tuseEffect(() => {\n\t\tlet active = true;\n\t\tconst doFetch = async (): Promise<void> => {\n\t\t\ttry {\n\t\t\t\tconst gs = (await fetchGameState()) as unknown as FlatGameState;\n\t\t\t\tif (active) {\n\t\t\t\t\tsetGameModeOn(!!gs.game_mode_on);\n\t\t\t\t\tif (gs.theme)\n\t\t\t\t\t\tdocument.documentElement.dataset.theme = gs.theme;\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t/* non-fatal */\n\t\t\t}\n\t\t};\n\t\tvoid doFetch();\n\t\tconst timer = setInterval(() => void doFetch(), 15000);\n\t\treturn () => {\n\t\t\tactive = false;\n\t\t\tclearInterval(timer);\n\t\t};\n\t}, []);\n\n\t/* Compute mega-cache banner data */\n\tconst megaRows = scores.mega_cache ?? [];\n\tlet bestMega: number | null = null;\n\tlet firstSeen: number | null = null;\n\tfor (const r of megaRows) {\n\t\tif (bestMega == null || r.value > bestMega) bestMega = r.value;\n\t\tconst m = trophyMeta(r.meta);\n\t\tconst fs = m.firstSeenTs ?? m.firstSeen ?? r.ts;\n\t\tif (firstSeen == null || fs < firstSeen) firstSeen = fs;\n\t}\n\tconst showMega = bestMega != null && bestMega > 100;\n\n\t/* Turns level badge */\n\tconst turnsRows = scores.turns ?? [];\n\tconst maxTurns = turnsRows.reduce((mx, r) => Math.max(mx, r.value), 0);\n\tconst lvl = Math.floor(Math.log2(maxTurns + 1)) + 1;\n\n\t/* Game empty state */\n\tconst hasData =\n\t\t(scores.cache?.length ?? 0) > 0 ||\n\t\t(scores.dedupe?.length ?? 0) > 0 ||\n\t\tturnsRows.length > 0 ||\n\t\tmegaRows.length > 0 ||\n\t\treposCount > 0;\n\n\treturn (\n\t\t<div className={`game-tab${gameModeOn ? \" game-mode-on\" : \"\"}`}>\n\t\t\t{megaToast != null && (\n\t\t\t\t<div className=\"mega-cache-toast show\">{megaToast}</div>\n\t\t\t)}\n\n\t\t\t{showMega && (\n\t\t\t\t<div className=\"mega-cache-banner\">\n\t\t\t\t\t{\"\\uD83E\\uDD67\"} MEGA CACHE! peak {fmtPct(bestMega!)} — first\n\t\t\t\t\treached {fmtDate(firstSeen)}\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{showMega && (\n\t\t\t\t<div className=\"achievement-tile unlocked\">\n\t\t\t\t\t{\"\\uD83C\\uDFC6\"} Opie's Wild Ride\n\t\t\t\t\t<span className=\"ach-detail\">\n\t\t\t\t\t\tbest {fmtPct(bestMega!)} · first {fmtDate(firstSeen)}\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t<h2>High Scores</h2>\n\n\t\t\t{!hasData && !loading && (\n\t\t\t\t<div className=\"game-empty\">\n\t\t\t\t\tNo scores yet — run a session with game mode on.\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{hasData && (\n\t\t\t\t<div className=\"game-leaderboards\">\n\t\t\t\t\t<LeaderboardCard\n\t\t\t\t\t\ttitle=\"Cache %\"\n\t\t\t\t\t\trows={scores.cache ?? []}\n\t\t\t\t\t\trenderValue={(r) => fmtPct(r.value)}\n\t\t\t\t\t/>\n\t\t\t\t\t<LeaderboardCard\n\t\t\t\t\t\ttitle=\"Dedupe (collapsed)\"\n\t\t\t\t\t\trows={scores.dedupe ?? []}\n\t\t\t\t\t\trenderValue={(r) => r.value.toLocaleString()}\n\t\t\t\t\t/>\n\t\t\t\t\t<LeaderboardCard\n\t\t\t\t\t\ttitle=\"Turns\"\n\t\t\t\t\t\trows={turnsRows}\n\t\t\t\t\t\tbadge={\n\t\t\t\t\t\t\tturnsRows.length > 0 ? (\n\t\t\t\t\t\t\t\t<span className=\"lvl-badge\">LVL {lvl}</span>\n\t\t\t\t\t\t\t) : undefined\n\t\t\t\t\t\t}\n\t\t\t\t\t\trenderValue={(r) => r.value.toLocaleString()}\n\t\t\t\t\t/>\n\t\t\t\t\t<LeaderboardCard\n\t\t\t\t\t\ttitle=\"MEGA CACHE trophies\"\n\t\t\t\t\t\trows={megaRows}\n\t\t\t\t\t\trenderValue={(r) => (\n\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t{fmtPct(r.value)}\n\t\t\t\t\t\t\t\t<span className=\"lb-meta\">\n\t\t\t\t\t\t\t\t\t{fmtDate(\n\t\t\t\t\t\t\t\t\t\ttrophyMeta(r.meta).firstSeenTs ??\n\t\t\t\t\t\t\t\t\t\t\ttrophyMeta(r.meta).firstSeen ??\n\t\t\t\t\t\t\t\t\t\t\tr.ts,\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t)}\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{reposCount > 0 && (\n\t\t\t\t<div className=\"repos-badge-line\">\n\t\t\t\t\t<span className=\"repos-badge\">{reposCount} repos</span>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t<h3>Achievements</h3>\n\t\t\t<AchievementTiles achievements={achievements} />\n\t\t</div>\n\t);\n}\n"],"names":["fmtPct","v","fmtDate","ts","trophyMeta","m","repoBasename","path","LeaderboardCard","title","rows","badge","renderValue","jsxs","jsx","r","i","GameTab","scores","setScores","useState","reposCount","setReposCount","achievements","setAchievements","gameModeOn","setGameModeOn","loading","setLoading","megaToast","setMegaToast","lastMegaTsRef","useRef","fetchAll","useCallback","cache","dedupe","turns","megaCache","repos","ach","fetchGameScores","fetchAchievements","newRow","useEffect","active","doFetch","timer","gs","fetchGameState","megaRows","bestMega","firstSeen","fs","showMega","turnsRows","maxTurns","mx","lvl","hasData","Fragment","AchievementTiles"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import{u as x,r as d,x as j,y as g,j as e,z as b}from"./index-BX0bcgLu.js";const N=[{key:"integrity-check",label:"Integrity Check",desc:"Run PRAGMA integrity_check",dangerous:!1},{key:"reconcile-dedup",label:"Reconcile Dedup Mirror",desc:"Fix orphan dedup rows + backfill refs",dangerous:!1,confirm:"Reconcile dedup_mirror (safe read/write)?"},{key:"checkpoint",label:"WAL Checkpoint",desc:"Merge WAL into main DB (read-only)",dangerous:!1},{key:"vacuum",label:"VACUUM",desc:"Rebuild DB reclaiming free pages",dangerous:!0,confirm:"VACUUM rewrites the entire DB. Continue?"},{key:"reindex",label:"REINDEX",desc:"Rebuild all indexes",dangerous:!0,confirm:"REINDEX rebuilds indexes. Continue?"},{key:"fts5-rebuild",label:"FTS5 Rebuild",desc:"Rebuild full-text search index",dangerous:!0,confirm:"Rebuild FTS5 trigram index. Continue?"},{key:"prune",label:"Prune Old Rows",desc:"Delete transcripts + epochs older than N days",dangerous:!0,daysOld:!0,confirm:"Delete old raw_transcript rows. Continue?"}];function o(s){return s<1024?`${s} B`:s<1024*1024?`${(s/1024).toFixed(1)} KB`:`${(s/(1024*1024)).toFixed(1)} MB`}function p({files:s,pageSize:t,pageCount:a,freelistPages:r}){const l=s.dbBytes+s.walBytes+s.shmBytes;return e.jsxs("div",{className:"stat-section",children:[e.jsx("div",{className:"stat-label",children:"File Sizes"}),e.jsx("table",{className:"compact-table",children:e.jsxs("tbody",{children:[e.jsxs("tr",{children:[e.jsx("td",{children:"sqlite.db"}),e.jsx("td",{className:"num",children:o(s.dbBytes)})]}),s.walBytes>0&&e.jsxs("tr",{children:[e.jsx("td",{children:".wal"}),e.jsx("td",{className:"num",children:o(s.walBytes)})]}),s.shmBytes>0&&e.jsxs("tr",{children:[e.jsx("td",{children:".shm"}),e.jsx("td",{className:"num",children:o(s.shmBytes)})]}),e.jsxs("tr",{children:[e.jsx("td",{children:"Total"}),e.jsx("td",{className:"num",children:o(l)})]}),e.jsxs("tr",{children:[e.jsx("td",{children:"Page size"}),e.jsx("td",{className:"num",children:o(t)})]}),e.jsxs("tr",{children:[e.jsx("td",{children:"Pages"}),e.jsxs("td",{className:"num",children:[a.toLocaleString()," (",r.toLocaleString()," ","free)"]})]})]})})]})}function y({tables:s}){const t=s.reduce((a,r)=>a+Math.max(0,r.rowCount),0);return e.jsxs("div",{className:"stat-section",children:[e.jsxs("div",{className:"stat-label",children:["Table Row Counts (",t.toLocaleString()," total)"]}),e.jsxs("table",{className:"compact-table",children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{children:"Table"}),e.jsx("th",{className:"num",children:"Rows"})]})}),e.jsx("tbody",{children:s.map(a=>e.jsxs("tr",{children:[e.jsx("td",{children:a.table}),e.jsx("td",{className:"num",children:a.rowCount>=0?a.rowCount.toLocaleString():"—"})]},a.table))})]})]})}function k({data:s,loading:t,error:a}){return e.jsxs("div",{className:"card",children:[e.jsx("div",{className:"card-header",children:"DB Stats"}),e.jsxs("div",{className:"card-body",children:[t&&!s&&e.jsx("span",{className:"text-muted",children:"Loading…"}),a&&!s&&e.jsx("span",{className:"text-error",children:a.message}),s&&e.jsxs(e.Fragment,{children:[e.jsx(p,{files:s.storage.files,pageSize:s.storage.pageSize,pageCount:s.storage.pageCount,freelistPages:s.storage.freelistPages}),e.jsx(y,{tables:s.tables})]})]})]})}function v({columns:s}){const t=s.filter(a=>!a.present);return e.jsxs("div",{className:"stat-section",children:[e.jsxs("div",{className:"stat-label",children:["Column Audit (",s.length," columns, ",t.length," missing)"]}),e.jsxs("table",{className:"compact-table",children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{children:"Table"}),e.jsx("th",{children:"Column"}),e.jsx("th",{children:"Decl"}),e.jsx("th",{children:"Status"})]})}),e.jsx("tbody",{children:s.map(a=>e.jsxs("tr",{children:[e.jsx("td",{children:a.table}),e.jsx("td",{children:a.column}),e.jsx("td",{className:"text-mono",children:a.expectedDecl}),e.jsx("td",{className:a.present?"text-ok":"text-error",children:a.present?"✓":"MISSING"})]},`${a.table}.${a.column}`))})]})]})}function C({data:s,loading:t,error:a}){return e.jsxs("div",{className:"card",children:[e.jsxs("div",{className:"card-header",children:["Schema Health",s&&e.jsx("span",{className:`badge ${s.healthy?"badge-ok":"badge-bad"}`,children:s.healthy?"HEALTHY":"DEGRADED"})]}),e.jsxs("div",{className:"card-body",children:[t&&!s&&e.jsx("span",{className:"text-muted",children:"Loading…"}),a&&!s&&e.jsx("span",{className:"text-error",children:a.message}),s&&e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"stat-row",children:[e.jsx("span",{className:"stat-label",children:"Schema version"}),e.jsx("span",{className:"num",children:s.schemaVersion})]}),e.jsxs("div",{className:"stat-row",children:[e.jsx("span",{className:"stat-label",children:"Integrity"}),e.jsx("span",{className:s.integrity[0]==="ok"?"text-ok":"text-error",children:s.integrity.join("; ")})]}),e.jsx(v,{columns:s.columns}),s.fkCheck.length>0&&e.jsxs("div",{className:"stat-section",children:[e.jsxs("div",{className:"stat-label text-warn",children:["FK Check (",s.fkCheck.length,")"]}),s.fkCheck.map((r,l)=>e.jsx("div",{className:"text-warn text-mono",children:r},l))]})]})]})]})}function f(){const[s,t]=d.useState([]),[a,r]=d.useState(null),[l,m]=d.useState("30"),u=d.useCallback(async n=>{const h=n.daysOld?{action:"prune",daysOld:Number(l)}:{action:n.key};r(n.key);try{const c=await b(h);t(i=>[{...c,timestamp:Date.now()},...i].slice(0,20))}catch(c){t(i=>[{operation:n.key,success:!1,affected:0,reclaimedBytes:0,summary:c instanceof Error?c.message:String(c),timestamp:Date.now()},...i].slice(0,20))}finally{r(null)}},[l]);return e.jsxs("div",{className:"card",children:[e.jsx("div",{className:"card-header",children:"Actions"}),e.jsx("div",{className:"card-body",children:e.jsx("div",{className:"action-grid",children:N.map(n=>{const h=a===n.key,c=s.find(i=>i.operation===n.key);return e.jsxs("div",{className:"action-item",children:[e.jsxs("div",{className:"action-info",children:[e.jsxs("div",{className:"action-label",children:[n.label,n.dangerous&&e.jsx("span",{className:"badge badge-warn",children:"DESTRUCTIVE"})]}),e.jsx("div",{className:"action-desc",children:n.desc}),c&&e.jsx("div",{className:`action-result ${c.success?"text-ok":"text-error"}`,children:c.summary})]}),e.jsxs("div",{className:"action-controls",children:[n.daysOld&&e.jsx("input",{type:"number",min:1,max:365,value:l,onChange:i=>m(i.target.value),className:"days-input",disabled:h}),e.jsx("button",{className:`btn ${n.dangerous?"btn-danger":"btn-primary"}`,disabled:a!==null,onClick:()=>{n.confirm&&!window.confirm(n.confirm)||u(n)},children:h?"Running…":"Run"})]})]},n.key)})})})]})}function S(){const{data:s,loading:t,error:a}=x(d.useCallback(()=>j(),[]),{pollInterval:15e3}),{data:r,loading:l,error:m}=x(d.useCallback(()=>g(),[]),{pollInterval:1e4});return e.jsxs("div",{className:"maintenance-tab",children:[e.jsxs("div",{className:"card-grid",children:[e.jsx(C,{data:r,loading:l,error:m}),e.jsx(k,{data:s,loading:t,error:a})]}),e.jsx(f,{})]})}export{S as default};
2
+ //# sourceMappingURL=MaintenanceTab-DLkKCztn.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MaintenanceTab-DLkKCztn.js","sources":["../../src/tabs/MaintenanceTab.tsx"],"sourcesContent":["/**\n * dashboard-client/src/tabs/MaintenanceTab.tsx — Maintenance tab (v0.11.2 S49B).\n *\n * Three cards:\n * 1. Schema Health (schema version + integrity + column audit)\n * 2. DB Stats (file sizes + table row counts)\n * 3. Actions (one-time maintenance operations with results history)\n *\n * All three fetch independently; failures in one do not block the others.\n */\n\nimport type React from \"react\";\nimport { useState, useCallback } from \"react\";\nimport type {\n\tDbStatsResponse,\n\tSchemaHealthResponse,\n\tMaintenanceAction,\n\tMaintenanceActionResult,\n} from \"@contracts\";\nimport { useApi } from \"../hooks/useApi\";\nimport {\n\tfetchDbStats,\n\tfetchSchemaHealth,\n\tpostMaintenanceAction,\n} from \"../api/client\";\n\n// ---------------------------------------------------------------------------\n// Action config\n// ---------------------------------------------------------------------------\n\ninterface ActionDef {\n\tkey: string;\n\tlabel: string;\n\tdesc: string;\n\tdangerous: boolean;\n\tconfirm?: string;\n\tdaysOld?: boolean;\n}\n\nconst ACTIONS: ActionDef[] = [\n\t{\n\t\tkey: \"integrity-check\",\n\t\tlabel: \"Integrity Check\",\n\t\tdesc: \"Run PRAGMA integrity_check\",\n\t\tdangerous: false,\n\t},\n\t{\n\t\tkey: \"reconcile-dedup\",\n\t\tlabel: \"Reconcile Dedup Mirror\",\n\t\tdesc: \"Fix orphan dedup rows + backfill refs\",\n\t\tdangerous: false,\n\t\tconfirm: \"Reconcile dedup_mirror (safe read/write)?\",\n\t},\n\t{\n\t\tkey: \"checkpoint\",\n\t\tlabel: \"WAL Checkpoint\",\n\t\tdesc: \"Merge WAL into main DB (read-only)\",\n\t\tdangerous: false,\n\t},\n\t{\n\t\tkey: \"vacuum\",\n\t\tlabel: \"VACUUM\",\n\t\tdesc: \"Rebuild DB reclaiming free pages\",\n\t\tdangerous: true,\n\t\tconfirm: \"VACUUM rewrites the entire DB. Continue?\",\n\t},\n\t{\n\t\tkey: \"reindex\",\n\t\tlabel: \"REINDEX\",\n\t\tdesc: \"Rebuild all indexes\",\n\t\tdangerous: true,\n\t\tconfirm: \"REINDEX rebuilds indexes. Continue?\",\n\t},\n\t{\n\t\tkey: \"fts5-rebuild\",\n\t\tlabel: \"FTS5 Rebuild\",\n\t\tdesc: \"Rebuild full-text search index\",\n\t\tdangerous: true,\n\t\tconfirm: \"Rebuild FTS5 trigram index. Continue?\",\n\t},\n\t{\n\t\tkey: \"prune\",\n\t\tlabel: \"Prune Old Rows\",\n\t\tdesc: \"Delete transcripts + epochs older than N days\",\n\t\tdangerous: true,\n\t\tdaysOld: true,\n\t\tconfirm: \"Delete old raw_transcript rows. Continue?\",\n\t},\n];\n\n// ---------------------------------------------------------------------------\n// Format helpers (local — matches html.ts fmtBytes)\n// ---------------------------------------------------------------------------\n\nfunction fmtBytes(b: number): string {\n\tif (b < 1024) return `${b} B`;\n\tif (b < 1024 * 1024) return `${(b / 1024).toFixed(1)} KB`;\n\treturn `${(b / (1024 * 1024)).toFixed(1)} MB`;\n}\n\n// ---------------------------------------------------------------------------\n// DB Stats card\n// ---------------------------------------------------------------------------\n\nfunction FileSizesCard({\n\tfiles,\n\tpageSize,\n\tpageCount,\n\tfreelistPages,\n}: {\n\tfiles: DbStatsResponse[\"storage\"][\"files\"];\n\tpageSize: number;\n\tpageCount: number;\n\tfreelistPages: number;\n}): React.ReactElement {\n\tconst total = files.dbBytes + files.walBytes + files.shmBytes;\n\treturn (\n\t\t<div className=\"stat-section\">\n\t\t\t<div className=\"stat-label\">File Sizes</div>\n\t\t\t<table className=\"compact-table\">\n\t\t\t\t<tbody>\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<td>sqlite.db</td>\n\t\t\t\t\t\t<td className=\"num\">{fmtBytes(files.dbBytes)}</td>\n\t\t\t\t\t</tr>\n\t\t\t\t\t{files.walBytes > 0 && (\n\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t<td>.wal</td>\n\t\t\t\t\t\t\t<td className=\"num\">{fmtBytes(files.walBytes)}</td>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t)}\n\t\t\t\t\t{files.shmBytes > 0 && (\n\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t<td>.shm</td>\n\t\t\t\t\t\t\t<td className=\"num\">{fmtBytes(files.shmBytes)}</td>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t)}\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<td>Total</td>\n\t\t\t\t\t\t<td className=\"num\">{fmtBytes(total)}</td>\n\t\t\t\t\t</tr>\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<td>Page size</td>\n\t\t\t\t\t\t<td className=\"num\">{fmtBytes(pageSize)}</td>\n\t\t\t\t\t</tr>\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<td>Pages</td>\n\t\t\t\t\t\t<td className=\"num\">\n\t\t\t\t\t\t\t{pageCount.toLocaleString()} ({freelistPages.toLocaleString()}{\" \"}\n\t\t\t\t\t\t\tfree)\n\t\t\t\t\t\t</td>\n\t\t\t\t\t</tr>\n\t\t\t\t</tbody>\n\t\t\t</table>\n\t\t</div>\n\t);\n}\n\nfunction TableRowCountsCard({\n\ttables,\n}: {\n\ttables: DbStatsResponse[\"tables\"];\n}): React.ReactElement {\n\tconst totalRows = tables.reduce((s, t) => s + Math.max(0, t.rowCount), 0);\n\treturn (\n\t\t<div className=\"stat-section\">\n\t\t\t<div className=\"stat-label\">\n\t\t\t\tTable Row Counts ({totalRows.toLocaleString()} total)\n\t\t\t</div>\n\t\t\t<table className=\"compact-table\">\n\t\t\t\t<thead>\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<th>Table</th>\n\t\t\t\t\t\t<th className=\"num\">Rows</th>\n\t\t\t\t\t</tr>\n\t\t\t\t</thead>\n\t\t\t\t<tbody>\n\t\t\t\t\t{tables.map((t) => (\n\t\t\t\t\t\t<tr key={t.table}>\n\t\t\t\t\t\t\t<td>{t.table}</td>\n\t\t\t\t\t\t\t<td className=\"num\">\n\t\t\t\t\t\t\t\t{t.rowCount >= 0 ? t.rowCount.toLocaleString() : \"—\"}\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t))}\n\t\t\t\t</tbody>\n\t\t\t</table>\n\t\t</div>\n\t);\n}\n\nfunction DbStatsCard({\n\tdata,\n\tloading,\n\terror,\n}: {\n\tdata: DbStatsResponse | null;\n\tloading: boolean;\n\terror: Error | null;\n}): React.ReactElement {\n\treturn (\n\t\t<div className=\"card\">\n\t\t\t<div className=\"card-header\">DB Stats</div>\n\t\t\t<div className=\"card-body\">\n\t\t\t\t{loading && !data && <span className=\"text-muted\">Loading…</span>}\n\t\t\t\t{error && !data && <span className=\"text-error\">{error.message}</span>}\n\t\t\t\t{data && (\n\t\t\t\t\t<>\n\t\t\t\t\t\t<FileSizesCard\n\t\t\t\t\t\t\tfiles={data.storage.files}\n\t\t\t\t\t\t\tpageSize={data.storage.pageSize}\n\t\t\t\t\t\t\tpageCount={data.storage.pageCount}\n\t\t\t\t\t\t\tfreelistPages={data.storage.freelistPages}\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t<TableRowCountsCard tables={data.tables} />\n\t\t\t\t\t</>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\n// ---------------------------------------------------------------------------\n// Schema Health card\n// ---------------------------------------------------------------------------\n\nfunction ColumnAuditCard({\n\tcolumns,\n}: {\n\tcolumns: SchemaHealthResponse[\"columns\"];\n}): React.ReactElement {\n\tconst missing = columns.filter((c) => !c.present);\n\treturn (\n\t\t<div className=\"stat-section\">\n\t\t\t<div className=\"stat-label\">\n\t\t\t\tColumn Audit ({columns.length} columns, {missing.length} missing)\n\t\t\t</div>\n\t\t\t<table className=\"compact-table\">\n\t\t\t\t<thead>\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<th>Table</th>\n\t\t\t\t\t\t<th>Column</th>\n\t\t\t\t\t\t<th>Decl</th>\n\t\t\t\t\t\t<th>Status</th>\n\t\t\t\t\t</tr>\n\t\t\t\t</thead>\n\t\t\t\t<tbody>\n\t\t\t\t\t{columns.map((c) => (\n\t\t\t\t\t\t<tr key={`${c.table}.${c.column}`}>\n\t\t\t\t\t\t\t<td>{c.table}</td>\n\t\t\t\t\t\t\t<td>{c.column}</td>\n\t\t\t\t\t\t\t<td className=\"text-mono\">{c.expectedDecl}</td>\n\t\t\t\t\t\t\t<td className={c.present ? \"text-ok\" : \"text-error\"}>\n\t\t\t\t\t\t\t\t{c.present ? \"✓\" : \"MISSING\"}\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t))}\n\t\t\t\t</tbody>\n\t\t\t</table>\n\t\t</div>\n\t);\n}\n\nfunction SchemaHealthCard({\n\tdata,\n\tloading,\n\terror,\n}: {\n\tdata: SchemaHealthResponse | null;\n\tloading: boolean;\n\terror: Error | null;\n}): React.ReactElement {\n\treturn (\n\t\t<div className=\"card\">\n\t\t\t<div className=\"card-header\">\n\t\t\t\tSchema Health\n\t\t\t\t{data && (\n\t\t\t\t\t<span className={`badge ${data.healthy ? \"badge-ok\" : \"badge-bad\"}`}>\n\t\t\t\t\t\t{data.healthy ? \"HEALTHY\" : \"DEGRADED\"}\n\t\t\t\t\t</span>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t\t<div className=\"card-body\">\n\t\t\t\t{loading && !data && <span className=\"text-muted\">Loading…</span>}\n\t\t\t\t{error && !data && <span className=\"text-error\">{error.message}</span>}\n\t\t\t\t{data && (\n\t\t\t\t\t<>\n\t\t\t\t\t\t<div className=\"stat-row\">\n\t\t\t\t\t\t\t<span className=\"stat-label\">Schema version</span>\n\t\t\t\t\t\t\t<span className=\"num\">{data.schemaVersion}</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div className=\"stat-row\">\n\t\t\t\t\t\t\t<span className=\"stat-label\">Integrity</span>\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tclassName={\n\t\t\t\t\t\t\t\t\tdata.integrity[0] === \"ok\" ? \"text-ok\" : \"text-error\"\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{data.integrity.join(\"; \")}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<ColumnAuditCard columns={data.columns} />\n\t\t\t\t\t\t{data.fkCheck.length > 0 && (\n\t\t\t\t\t\t\t<div className=\"stat-section\">\n\t\t\t\t\t\t\t\t<div className=\"stat-label text-warn\">\n\t\t\t\t\t\t\t\t\tFK Check ({data.fkCheck.length})\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t{data.fkCheck.map((line, i) => (\n\t\t\t\t\t\t\t\t\t<div key={i} className=\"text-warn text-mono\">\n\t\t\t\t\t\t\t\t\t\t{line}\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\n// ---------------------------------------------------------------------------\n// Actions card\n// ---------------------------------------------------------------------------\n\nfunction ActionsCard(): React.ReactElement {\n\tconst [results, setResults] = useState<MaintenanceActionResult[]>([]);\n\tconst [running, setRunning] = useState<string | null>(null);\n\tconst [days, setDays] = useState(\"30\");\n\n\tconst handleAction = useCallback(\n\t\tasync (def: ActionDef) => {\n\t\t\tconst action: MaintenanceAction = def.daysOld\n\t\t\t\t? { action: \"prune\", daysOld: Number(days) }\n\t\t\t\t: ({ action: def.key } as MaintenanceAction);\n\n\t\t\tsetRunning(def.key);\n\t\t\ttry {\n\t\t\t\tconst res = await postMaintenanceAction(action);\n\t\t\t\tsetResults((prev) =>\n\t\t\t\t\t[{ ...res, timestamp: Date.now() }, ...prev].slice(0, 20),\n\t\t\t\t);\n\t\t\t} catch (err) {\n\t\t\t\tsetResults((prev) =>\n\t\t\t\t\t[\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\toperation: def.key,\n\t\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\t\taffected: 0,\n\t\t\t\t\t\t\treclaimedBytes: 0,\n\t\t\t\t\t\t\tsummary: err instanceof Error ? err.message : String(err),\n\t\t\t\t\t\t\ttimestamp: Date.now(),\n\t\t\t\t\t\t},\n\t\t\t\t\t\t...prev,\n\t\t\t\t\t].slice(0, 20),\n\t\t\t\t);\n\t\t\t} finally {\n\t\t\t\tsetRunning(null);\n\t\t\t}\n\t\t},\n\t\t[days],\n\t);\n\n\treturn (\n\t\t<div className=\"card\">\n\t\t\t<div className=\"card-header\">Actions</div>\n\t\t\t<div className=\"card-body\">\n\t\t\t\t<div className=\"action-grid\">\n\t\t\t\t\t{ACTIONS.map((def) => {\n\t\t\t\t\t\tconst isRunning = running === def.key;\n\t\t\t\t\t\tconst lastResult = results.find((r) => r.operation === def.key);\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t<div key={def.key} className=\"action-item\">\n\t\t\t\t\t\t\t\t<div className=\"action-info\">\n\t\t\t\t\t\t\t\t\t<div className=\"action-label\">\n\t\t\t\t\t\t\t\t\t\t{def.label}\n\t\t\t\t\t\t\t\t\t\t{def.dangerous && (\n\t\t\t\t\t\t\t\t\t\t\t<span className=\"badge badge-warn\">DESTRUCTIVE</span>\n\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t<div className=\"action-desc\">{def.desc}</div>\n\t\t\t\t\t\t\t\t\t{lastResult && (\n\t\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\t\tclassName={`action-result ${lastResult.success ? \"text-ok\" : \"text-error\"}`}\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t{lastResult.summary}\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t<div className=\"action-controls\">\n\t\t\t\t\t\t\t\t\t{def.daysOld && (\n\t\t\t\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\t\t\t\ttype=\"number\"\n\t\t\t\t\t\t\t\t\t\t\tmin={1}\n\t\t\t\t\t\t\t\t\t\t\tmax={365}\n\t\t\t\t\t\t\t\t\t\t\tvalue={days}\n\t\t\t\t\t\t\t\t\t\t\tonChange={(e) => setDays(e.target.value)}\n\t\t\t\t\t\t\t\t\t\t\tclassName=\"days-input\"\n\t\t\t\t\t\t\t\t\t\t\tdisabled={isRunning}\n\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\tclassName={`btn ${def.dangerous ? \"btn-danger\" : \"btn-primary\"}`}\n\t\t\t\t\t\t\t\t\t\tdisabled={running !== null}\n\t\t\t\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\t\t\t\tif (def.confirm && !window.confirm(def.confirm)) return;\n\t\t\t\t\t\t\t\t\t\t\thandleAction(def);\n\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{isRunning ? \"Running…\" : \"Run\"}\n\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t);\n\t\t\t\t\t})}\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\n// ---------------------------------------------------------------------------\n// Main tab component\n// ---------------------------------------------------------------------------\n\nexport default function MaintenanceTab(): React.ReactElement {\n\tconst {\n\t\tdata: dbStats,\n\t\tloading: statsLoading,\n\t\terror: statsError,\n\t} = useApi<DbStatsResponse>(\n\t\tuseCallback(() => fetchDbStats(), []),\n\t\t{ pollInterval: 15000 },\n\t);\n\n\tconst {\n\t\tdata: schemaHealth,\n\t\tloading: healthLoading,\n\t\terror: healthError,\n\t} = useApi<SchemaHealthResponse>(\n\t\tuseCallback(() => fetchSchemaHealth(), []),\n\t\t{ pollInterval: 10000 },\n\t);\n\n\treturn (\n\t\t<div className=\"maintenance-tab\">\n\t\t\t<div className=\"card-grid\">\n\t\t\t\t<SchemaHealthCard\n\t\t\t\t\tdata={schemaHealth}\n\t\t\t\t\tloading={healthLoading}\n\t\t\t\t\terror={healthError}\n\t\t\t\t/>\n\t\t\t\t<DbStatsCard data={dbStats} loading={statsLoading} error={statsError} />\n\t\t\t</div>\n\t\t\t<ActionsCard />\n\t\t</div>\n\t);\n}\n"],"names":["ACTIONS","fmtBytes","b","FileSizesCard","files","pageSize","pageCount","freelistPages","total","jsxs","jsx","TableRowCountsCard","tables","totalRows","s","t","DbStatsCard","data","loading","error","Fragment","ColumnAuditCard","columns","missing","c","SchemaHealthCard","line","i","ActionsCard","results","setResults","useState","running","setRunning","days","setDays","handleAction","useCallback","def","action","res","postMaintenanceAction","prev","err","isRunning","lastResult","r","e","MaintenanceTab","dbStats","statsLoading","statsError","useApi","fetchDbStats","schemaHealth","healthLoading","healthError","fetchSchemaHealth"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import{j as e,u as o,r as x,e as j,c as N}from"./index-BcoSz-L4.js";function i(s){return s>=1e3?`${(s/1e3).toFixed(1)}s`:`${s.toFixed(0)}ms`}function c({value:s,max:a,colorClass:r}){const p=a>0?Math.max(0,Math.min(100,s/a*100)):0;return e.jsx("div",{className:"perf-bar-track",children:e.jsx("div",{className:`perf-bar-fill ${r}`,style:{width:`${p}%`}})})}function _({perf:s}){const a=Math.max(s.turn_latency_ms.p95,s.provider_latency_ms.p95,1),r=Math.max(s.tps.avg,1);return e.jsxs("div",{className:"perf-chart",children:[e.jsxs("div",{className:"perf-header",children:[e.jsx("h3",{children:"Performance"}),e.jsxs("span",{className:"perf-window",children:[s.windowMinutes,"min window · ",s.sampleCount," samples"]})]}),e.jsxs("div",{className:"perf-grid",children:[e.jsxs("div",{className:"perf-metric",children:[e.jsx("span",{className:"perf-label",children:"Turn latency p50 / p95"}),e.jsxs("span",{className:"perf-value",children:[i(s.turn_latency_ms.p50)," /"," ",i(s.turn_latency_ms.p95)]}),e.jsx(c,{value:s.turn_latency_ms.p95,max:a,colorClass:"perf-blue"})]}),e.jsxs("div",{className:"perf-metric",children:[e.jsx("span",{className:"perf-label",children:"Provider latency p50 / p95"}),e.jsxs("span",{className:"perf-value",children:[i(s.provider_latency_ms.p50)," /"," ",i(s.provider_latency_ms.p95)]}),e.jsx(c,{value:s.provider_latency_ms.p95,max:a,colorClass:"perf-purple"})]}),e.jsxs("div",{className:"perf-metric",children:[e.jsx("span",{className:"perf-label",children:"Tokens/sec (avg)"}),e.jsx("span",{className:"perf-value",children:s.tps.avg.toFixed(1)}),e.jsx(c,{value:s.tps.avg,max:r,colorClass:"perf-green"})]}),e.jsxs("div",{className:"perf-metric",children:[e.jsx("span",{className:"perf-label",children:"Cache hit (avg / latest)"}),e.jsxs("span",{className:"perf-value",children:[s.cache_hit_pct.avg.toFixed(1),"% /"," ",s.cache_hit_pct.latest.toFixed(1),"%"]}),e.jsx(c,{value:s.cache_hit_pct.avg,max:100,colorClass:"perf-green"})]}),e.jsxs("div",{className:"perf-metric",children:[e.jsx("span",{className:"perf-label",children:"DB recompute p95"}),e.jsx("span",{className:"perf-value",children:i(s.db_recompute_ms.p95)}),e.jsx(c,{value:s.db_recompute_ms.p95,max:a,colorClass:"perf-yellow"})]}),e.jsxs("div",{className:"perf-metric",children:[e.jsx("span",{className:"perf-label",children:"Disk write p95"}),e.jsx("span",{className:"perf-value",children:i(s.disk_write_ms.p95)}),e.jsx(c,{value:s.disk_write_ms.p95,max:a,colorClass:"perf-yellow"})]})]}),e.jsxs("div",{className:"perf-resources",children:[e.jsxs("div",{className:"perf-metric",children:[e.jsx("span",{className:"perf-label",children:"RSS"}),e.jsxs("span",{className:"perf-value",children:[s.rss_mb.latest.toFixed(0)," MB"]})]}),e.jsxs("div",{className:"perf-metric",children:[e.jsx("span",{className:"perf-label",children:"Heap"}),e.jsxs("span",{className:"perf-value",children:[s.heap_mb.latest.toFixed(0)," MB"]})]}),e.jsxs("div",{className:"perf-metric",children:[e.jsx("span",{className:"perf-label",children:"CPU user"}),e.jsxs("span",{className:"perf-value",children:[(s.cpu_user_ms.latest/1e3).toFixed(1),"s"]})]}),e.jsxs("div",{className:"perf-metric",children:[e.jsx("span",{className:"perf-label",children:"CPU sys"}),e.jsxs("span",{className:"perf-value",children:[(s.cpu_sys_ms.latest/1e3).toFixed(1),"s"]})]})]}),s.diag&&e.jsxs("div",{className:"perf-diag",children:[e.jsxs("span",{className:"perf-label",children:["Fast-gate fires: ",s.diag.ctxFastGate]}),e.jsxs("span",{className:"perf-label",children:["Live trim fires: ",s.diag.liveTrimFires]}),e.jsxs("span",{className:"perf-label",children:["Live trim replays: ",s.diag.liveTrimReplays]})]})]})}function t(s){return s==null?"—":s>=100?`${Math.round(s)}ms`:`${s.toFixed(1)}ms`}function n(s,a){return s==null||typeof s!="number"?"—":s.toFixed(a)}function u(s){return s==null?"—":String(s)}function l({label:s,value:a}){return e.jsxs("div",{className:"perf-metric",children:[e.jsx("span",{className:"perf-label",children:s}),e.jsx("span",{className:"perf-value",children:a})]})}function d({title:s,children:a}){return e.jsxs("div",{className:"perf-card",children:[e.jsx("h3",{className:"perf-card-title",children:s}),e.jsx("div",{className:"perf-card-body",children:a})]})}function f({perf:s}){const a=`${n(s.cpu_user_ms.latest,1)} / ${n(s.cpu_sys_ms.latest,1)} ms`;return e.jsxs("div",{className:"perf-cards-grid",children:[e.jsxs(d,{title:"Model latency",children:[e.jsx(l,{label:"Turn p50",value:t(s.turn_latency_ms.p50)}),e.jsx(l,{label:"Turn p95",value:t(s.turn_latency_ms.p95)}),e.jsx(l,{label:"Provider p50",value:t(s.provider_latency_ms.p50)}),e.jsx(l,{label:"Provider p95",value:t(s.provider_latency_ms.p95)})]}),e.jsxs(d,{title:"Throughput",children:[e.jsx(l,{label:"TPS (avg)",value:n(s.tps.avg,1)}),e.jsx(l,{label:"Cache hit %",value:`${n(s.cache_hit_pct.avg,1)}%`})]}),e.jsxs(d,{title:"Process",children:[e.jsx(l,{label:"RSS",value:`${n(s.rss_mb.latest,1)} MB`}),e.jsx(l,{label:"Heap",value:`${n(s.heap_mb.latest,1)} MB`}),e.jsx(l,{label:"CPU user / sys",value:a})]}),e.jsxs(d,{title:"Snapshot cost",children:[e.jsx(l,{label:"DB recompute p50",value:t(s.db_recompute_ms.p50)}),e.jsx(l,{label:"DB recompute p95",value:t(s.db_recompute_ms.p95)}),e.jsx(l,{label:"Disk write p50",value:t(s.disk_write_ms.p50)})]}),e.jsxs(d,{title:"TUI lag proxy",children:[e.jsx(l,{label:"Live-trim fires",value:u(s.diag?.liveTrimFires)}),e.jsx(l,{label:"Cache replays",value:u(s.diag?.liveTrimReplays)}),e.jsx(l,{label:"Fast-gate skips",value:u(s.diag?.ctxFastGate)}),e.jsx("span",{className:"perf-card-note",children:"skip vs recompute vs replay cadence"})]})]})}function m({label:s,value:a,title:r}){return e.jsxs("div",{className:"model-field",title:r,children:[e.jsx("span",{className:"model-field-label",children:s}),e.jsx("span",{className:"model-field-value",children:a})]})}function b({name:s,providerName:a,provider:r,inputRate:p,outputRate:h}){const v=a||r||"—";return e.jsxs("div",{className:"model-badge",children:[e.jsx(m,{label:"Model",value:s,title:"The model pi is currently using — its pricing drives the cost figure."}),e.jsx(m,{label:"Provider",value:v,title:"The provider serving the model."}),e.jsx(m,{label:"Input Rate",value:`$${p.toFixed(6)}`,title:"USD per input token, from the model's pricing."}),e.jsx(m,{label:"Output Rate",value:`$${h.toFixed(6)}`,title:"USD per output token, from the model's pricing."})]})}function y(){const{data:s,error:a}=o(x.useCallback(()=>j({minutes:30}),[]),{pollInterval:1e4}),{data:r}=o(x.useCallback(()=>N(),[]),{pollInterval:1e4});return a&&!s?e.jsxs("div",{className:"tab-stub",children:["Error loading perf: ",a.message]}):s?e.jsxs("div",{className:"metrics-tab",children:[r?.model&&e.jsx(b,{name:r.model.name,providerName:r.model.providerName,provider:r.model.provider,inputRate:r.model.inputRate,outputRate:r.model.outputRate}),e.jsx(_,{perf:s}),e.jsx(f,{perf:s}),e.jsxs("div",{className:"perf-footer",children:[s.sampleCount," samples · updated ",s.updatedAt]})]}):e.jsx("div",{className:"tab-stub",children:"Loading perf…"})}export{y as default};
2
- //# sourceMappingURL=MetricsTab-BFo7pKM-.js.map
1
+ import{j as e,u as o,r as x,e as j,c as N}from"./index-BX0bcgLu.js";function i(s){return s>=1e3?`${(s/1e3).toFixed(1)}s`:`${s.toFixed(0)}ms`}function c({value:s,max:a,colorClass:r}){const p=a>0?Math.max(0,Math.min(100,s/a*100)):0;return e.jsx("div",{className:"perf-bar-track",children:e.jsx("div",{className:`perf-bar-fill ${r}`,style:{width:`${p}%`}})})}function _({perf:s}){const a=Math.max(s.turn_latency_ms.p95,s.provider_latency_ms.p95,1),r=Math.max(s.tps.avg,1);return e.jsxs("div",{className:"perf-chart",children:[e.jsxs("div",{className:"perf-header",children:[e.jsx("h3",{children:"Performance"}),e.jsxs("span",{className:"perf-window",children:[s.windowMinutes,"min window · ",s.sampleCount," samples"]})]}),e.jsxs("div",{className:"perf-grid",children:[e.jsxs("div",{className:"perf-metric",children:[e.jsx("span",{className:"perf-label",children:"Turn latency p50 / p95"}),e.jsxs("span",{className:"perf-value",children:[i(s.turn_latency_ms.p50)," /"," ",i(s.turn_latency_ms.p95)]}),e.jsx(c,{value:s.turn_latency_ms.p95,max:a,colorClass:"perf-blue"})]}),e.jsxs("div",{className:"perf-metric",children:[e.jsx("span",{className:"perf-label",children:"Provider latency p50 / p95"}),e.jsxs("span",{className:"perf-value",children:[i(s.provider_latency_ms.p50)," /"," ",i(s.provider_latency_ms.p95)]}),e.jsx(c,{value:s.provider_latency_ms.p95,max:a,colorClass:"perf-purple"})]}),e.jsxs("div",{className:"perf-metric",children:[e.jsx("span",{className:"perf-label",children:"Tokens/sec (avg)"}),e.jsx("span",{className:"perf-value",children:s.tps.avg.toFixed(1)}),e.jsx(c,{value:s.tps.avg,max:r,colorClass:"perf-green"})]}),e.jsxs("div",{className:"perf-metric",children:[e.jsx("span",{className:"perf-label",children:"Cache hit (avg / latest)"}),e.jsxs("span",{className:"perf-value",children:[s.cache_hit_pct.avg.toFixed(1),"% /"," ",s.cache_hit_pct.latest.toFixed(1),"%"]}),e.jsx(c,{value:s.cache_hit_pct.avg,max:100,colorClass:"perf-green"})]}),e.jsxs("div",{className:"perf-metric",children:[e.jsx("span",{className:"perf-label",children:"DB recompute p95"}),e.jsx("span",{className:"perf-value",children:i(s.db_recompute_ms.p95)}),e.jsx(c,{value:s.db_recompute_ms.p95,max:a,colorClass:"perf-yellow"})]}),e.jsxs("div",{className:"perf-metric",children:[e.jsx("span",{className:"perf-label",children:"Disk write p95"}),e.jsx("span",{className:"perf-value",children:i(s.disk_write_ms.p95)}),e.jsx(c,{value:s.disk_write_ms.p95,max:a,colorClass:"perf-yellow"})]})]}),e.jsxs("div",{className:"perf-resources",children:[e.jsxs("div",{className:"perf-metric",children:[e.jsx("span",{className:"perf-label",children:"RSS"}),e.jsxs("span",{className:"perf-value",children:[s.rss_mb.latest.toFixed(0)," MB"]})]}),e.jsxs("div",{className:"perf-metric",children:[e.jsx("span",{className:"perf-label",children:"Heap"}),e.jsxs("span",{className:"perf-value",children:[s.heap_mb.latest.toFixed(0)," MB"]})]}),e.jsxs("div",{className:"perf-metric",children:[e.jsx("span",{className:"perf-label",children:"CPU user"}),e.jsxs("span",{className:"perf-value",children:[(s.cpu_user_ms.latest/1e3).toFixed(1),"s"]})]}),e.jsxs("div",{className:"perf-metric",children:[e.jsx("span",{className:"perf-label",children:"CPU sys"}),e.jsxs("span",{className:"perf-value",children:[(s.cpu_sys_ms.latest/1e3).toFixed(1),"s"]})]})]}),s.diag&&e.jsxs("div",{className:"perf-diag",children:[e.jsxs("span",{className:"perf-label",children:["Fast-gate fires: ",s.diag.ctxFastGate]}),e.jsxs("span",{className:"perf-label",children:["Live trim fires: ",s.diag.liveTrimFires]}),e.jsxs("span",{className:"perf-label",children:["Live trim replays: ",s.diag.liveTrimReplays]})]})]})}function t(s){return s==null?"—":s>=100?`${Math.round(s)}ms`:`${s.toFixed(1)}ms`}function n(s,a){return s==null||typeof s!="number"?"—":s.toFixed(a)}function u(s){return s==null?"—":String(s)}function l({label:s,value:a}){return e.jsxs("div",{className:"perf-metric",children:[e.jsx("span",{className:"perf-label",children:s}),e.jsx("span",{className:"perf-value",children:a})]})}function d({title:s,children:a}){return e.jsxs("div",{className:"perf-card",children:[e.jsx("h3",{className:"perf-card-title",children:s}),e.jsx("div",{className:"perf-card-body",children:a})]})}function f({perf:s}){const a=`${n(s.cpu_user_ms.latest,1)} / ${n(s.cpu_sys_ms.latest,1)} ms`;return e.jsxs("div",{className:"perf-cards-grid",children:[e.jsxs(d,{title:"Model latency",children:[e.jsx(l,{label:"Turn p50",value:t(s.turn_latency_ms.p50)}),e.jsx(l,{label:"Turn p95",value:t(s.turn_latency_ms.p95)}),e.jsx(l,{label:"Provider p50",value:t(s.provider_latency_ms.p50)}),e.jsx(l,{label:"Provider p95",value:t(s.provider_latency_ms.p95)})]}),e.jsxs(d,{title:"Throughput",children:[e.jsx(l,{label:"TPS (avg)",value:n(s.tps.avg,1)}),e.jsx(l,{label:"Cache hit %",value:`${n(s.cache_hit_pct.avg,1)}%`})]}),e.jsxs(d,{title:"Process",children:[e.jsx(l,{label:"RSS",value:`${n(s.rss_mb.latest,1)} MB`}),e.jsx(l,{label:"Heap",value:`${n(s.heap_mb.latest,1)} MB`}),e.jsx(l,{label:"CPU user / sys",value:a})]}),e.jsxs(d,{title:"Snapshot cost",children:[e.jsx(l,{label:"DB recompute p50",value:t(s.db_recompute_ms.p50)}),e.jsx(l,{label:"DB recompute p95",value:t(s.db_recompute_ms.p95)}),e.jsx(l,{label:"Disk write p50",value:t(s.disk_write_ms.p50)})]}),e.jsxs(d,{title:"TUI lag proxy",children:[e.jsx(l,{label:"Live-trim fires",value:u(s.diag?.liveTrimFires)}),e.jsx(l,{label:"Cache replays",value:u(s.diag?.liveTrimReplays)}),e.jsx(l,{label:"Fast-gate skips",value:u(s.diag?.ctxFastGate)}),e.jsx("span",{className:"perf-card-note",children:"skip vs recompute vs replay cadence"})]})]})}function m({label:s,value:a,title:r}){return e.jsxs("div",{className:"model-field",title:r,children:[e.jsx("span",{className:"model-field-label",children:s}),e.jsx("span",{className:"model-field-value",children:a})]})}function b({name:s,providerName:a,provider:r,inputRate:p,outputRate:h}){const v=a||r||"—";return e.jsxs("div",{className:"model-badge",children:[e.jsx(m,{label:"Model",value:s,title:"The model pi is currently using — its pricing drives the cost figure."}),e.jsx(m,{label:"Provider",value:v,title:"The provider serving the model."}),e.jsx(m,{label:"Input Rate",value:`$${p.toFixed(6)}`,title:"USD per input token, from the model's pricing."}),e.jsx(m,{label:"Output Rate",value:`$${h.toFixed(6)}`,title:"USD per output token, from the model's pricing."})]})}function y(){const{data:s,error:a}=o(x.useCallback(()=>j({minutes:30}),[]),{pollInterval:1e4}),{data:r}=o(x.useCallback(()=>N(),[]),{pollInterval:1e4});return a&&!s?e.jsxs("div",{className:"tab-stub",children:["Error loading perf: ",a.message]}):s?e.jsxs("div",{className:"metrics-tab",children:[r?.model&&e.jsx(b,{name:r.model.name,providerName:r.model.providerName,provider:r.model.provider,inputRate:r.model.inputRate,outputRate:r.model.outputRate}),e.jsx(_,{perf:s}),e.jsx(f,{perf:s}),e.jsxs("div",{className:"perf-footer",children:[s.sampleCount," samples · updated ",s.updatedAt]})]}):e.jsx("div",{className:"tab-stub",children:"Loading perf…"})}export{y as default};
2
+ //# sourceMappingURL=MetricsTab-Cq4u-rZF.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"MetricsTab-BFo7pKM-.js","sources":["../../src/components/PerfChart.tsx","../../src/components/PerfCards.tsx","../../src/components/ModelBadge.tsx","../../src/tabs/MetricsTab.tsx"],"sourcesContent":["/**\n * dashboard-client/src/components/PerfChart.tsx — perf stat chart.\n *\n * The /api/perf endpoint returns rolling-window aggregates (not time-series),\n * so this renders a stat-card \"chart\": p50/p95 latency bars, TPS, cache hit %,\n * db recompute + disk write. Each metric shows a normalized bar + numeric.\n */\n\nimport type React from \"react\";\nimport type { PerfResponse } from \"@contracts\";\n\nexport interface PerfChartProps {\n\tperf: PerfResponse;\n}\n\nfunction fmtMs(n: number): string {\n\treturn n >= 1000 ? `${(n / 1000).toFixed(1)}s` : `${n.toFixed(0)}ms`;\n}\n\ninterface BarProps {\n\t/** Current value. */\n\tvalue: number;\n\t/** Value that represents 100% bar fill (the \"max\" reference). */\n\tmax: number;\n\t/** Color class. */\n\tcolorClass: string;\n}\n\nfunction Bar({ value, max, colorClass }: BarProps): React.ReactElement {\n\tconst pct = max > 0 ? Math.max(0, Math.min(100, (value / max) * 100)) : 0;\n\treturn (\n\t\t<div className=\"perf-bar-track\">\n\t\t\t<div\n\t\t\t\tclassName={`perf-bar-fill ${colorClass}`}\n\t\t\t\tstyle={{ width: `${pct}%` }}\n\t\t\t/>\n\t\t</div>\n\t);\n}\n\nexport function PerfChart({ perf }: PerfChartProps): React.ReactElement {\n\t// Reference maxes: p95 is the upper bound for latency bars; rates scale to a\n\t// reasonable reference (tok/s) so bars are visually meaningful even with\n\t// small samples. cache_hit_pct is already 0-100.\n\tconst latencyMax = Math.max(\n\t\tperf.turn_latency_ms.p95,\n\t\tperf.provider_latency_ms.p95,\n\t\t1,\n\t);\n\tconst tpsRef = Math.max(perf.tps.avg, 1);\n\n\treturn (\n\t\t<div className=\"perf-chart\">\n\t\t\t<div className=\"perf-header\">\n\t\t\t\t<h3>Performance</h3>\n\t\t\t\t<span className=\"perf-window\">\n\t\t\t\t\t{perf.windowMinutes}min window · {perf.sampleCount} samples\n\t\t\t\t</span>\n\t\t\t</div>\n\t\t\t<div className=\"perf-grid\">\n\t\t\t\t<div className=\"perf-metric\">\n\t\t\t\t\t<span className=\"perf-label\">Turn latency p50 / p95</span>\n\t\t\t\t\t<span className=\"perf-value\">\n\t\t\t\t\t\t{fmtMs(perf.turn_latency_ms.p50)} /{\" \"}\n\t\t\t\t\t\t{fmtMs(perf.turn_latency_ms.p95)}\n\t\t\t\t\t</span>\n\t\t\t\t\t<Bar\n\t\t\t\t\t\tvalue={perf.turn_latency_ms.p95}\n\t\t\t\t\t\tmax={latencyMax}\n\t\t\t\t\t\tcolorClass=\"perf-blue\"\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"perf-metric\">\n\t\t\t\t\t<span className=\"perf-label\">Provider latency p50 / p95</span>\n\t\t\t\t\t<span className=\"perf-value\">\n\t\t\t\t\t\t{fmtMs(perf.provider_latency_ms.p50)} /{\" \"}\n\t\t\t\t\t\t{fmtMs(perf.provider_latency_ms.p95)}\n\t\t\t\t\t</span>\n\t\t\t\t\t<Bar\n\t\t\t\t\t\tvalue={perf.provider_latency_ms.p95}\n\t\t\t\t\t\tmax={latencyMax}\n\t\t\t\t\t\tcolorClass=\"perf-purple\"\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"perf-metric\">\n\t\t\t\t\t<span className=\"perf-label\">Tokens/sec (avg)</span>\n\t\t\t\t\t<span className=\"perf-value\">{perf.tps.avg.toFixed(1)}</span>\n\t\t\t\t\t<Bar value={perf.tps.avg} max={tpsRef} colorClass=\"perf-green\" />\n\t\t\t\t</div>\n\t\t\t\t<div className=\"perf-metric\">\n\t\t\t\t\t<span className=\"perf-label\">Cache hit (avg / latest)</span>\n\t\t\t\t\t<span className=\"perf-value\">\n\t\t\t\t\t\t{perf.cache_hit_pct.avg.toFixed(1)}% /{\" \"}\n\t\t\t\t\t\t{perf.cache_hit_pct.latest.toFixed(1)}%\n\t\t\t\t\t</span>\n\t\t\t\t\t<Bar\n\t\t\t\t\t\tvalue={perf.cache_hit_pct.avg}\n\t\t\t\t\t\tmax={100}\n\t\t\t\t\t\tcolorClass=\"perf-green\"\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"perf-metric\">\n\t\t\t\t\t<span className=\"perf-label\">DB recompute p95</span>\n\t\t\t\t\t<span className=\"perf-value\">{fmtMs(perf.db_recompute_ms.p95)}</span>\n\t\t\t\t\t<Bar\n\t\t\t\t\t\tvalue={perf.db_recompute_ms.p95}\n\t\t\t\t\t\tmax={latencyMax}\n\t\t\t\t\t\tcolorClass=\"perf-yellow\"\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"perf-metric\">\n\t\t\t\t\t<span className=\"perf-label\">Disk write p95</span>\n\t\t\t\t\t<span className=\"perf-value\">{fmtMs(perf.disk_write_ms.p95)}</span>\n\t\t\t\t\t<Bar\n\t\t\t\t\t\tvalue={perf.disk_write_ms.p95}\n\t\t\t\t\t\tmax={latencyMax}\n\t\t\t\t\t\tcolorClass=\"perf-yellow\"\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<div className=\"perf-resources\">\n\t\t\t\t<div className=\"perf-metric\">\n\t\t\t\t\t<span className=\"perf-label\">RSS</span>\n\t\t\t\t\t<span className=\"perf-value\">{perf.rss_mb.latest.toFixed(0)} MB</span>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"perf-metric\">\n\t\t\t\t\t<span className=\"perf-label\">Heap</span>\n\t\t\t\t\t<span className=\"perf-value\">\n\t\t\t\t\t\t{perf.heap_mb.latest.toFixed(0)} MB\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"perf-metric\">\n\t\t\t\t\t<span className=\"perf-label\">CPU user</span>\n\t\t\t\t\t<span className=\"perf-value\">\n\t\t\t\t\t\t{(perf.cpu_user_ms.latest / 1000).toFixed(1)}s\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"perf-metric\">\n\t\t\t\t\t<span className=\"perf-label\">CPU sys</span>\n\t\t\t\t\t<span className=\"perf-value\">\n\t\t\t\t\t\t{(perf.cpu_sys_ms.latest / 1000).toFixed(1)}s\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t{perf.diag && (\n\t\t\t\t<div className=\"perf-diag\">\n\t\t\t\t\t<span className=\"perf-label\">\n\t\t\t\t\t\tFast-gate fires: {perf.diag.ctxFastGate}\n\t\t\t\t\t</span>\n\t\t\t\t\t<span className=\"perf-label\">\n\t\t\t\t\t\tLive trim fires: {perf.diag.liveTrimFires}\n\t\t\t\t\t</span>\n\t\t\t\t\t<span className=\"perf-label\">\n\t\t\t\t\t\tLive trim replays: {perf.diag.liveTrimReplays}\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n","/**\n * dashboard-client/src/components/PerfCards.tsx — 5 perf metric cards.\n *\n * Mirrors the old html.ts perf grid: model latency, throughput, process,\n * snapshot cost, and TUI lag proxy. Uses the existing perf-metric CSS\n * classes from repos-metrics.css plus metrics-extra.css for card headers.\n */\n\nimport type React from \"react\";\nimport type { PerfResponse } from \"@contracts\";\n\n/** Format milliseconds (em-dash for null/undefined). */\nfunction fmtMs(v: number | null | undefined): string {\n\treturn v == null ? \"\\u2014\" : v >= 100 ? `${Math.round(v)}ms` : `${v.toFixed(1)}ms`;\n}\n\n/** Format a number with fixed decimals (em-dash for null/non-number). */\nfunction fmtNum(v: number | null | undefined, dec: number): string {\n\treturn v == null || typeof v !== \"number\" ? \"\\u2014\" : v.toFixed(dec);\n}\n\n/** Format a diag counter (em-dash for null). */\nfunction fmtDiag(v: number | null | undefined): string {\n\treturn v == null ? \"\\u2014\" : String(v);\n}\n\ninterface PerfCardsProps {\n\tperf: PerfResponse;\n}\n\n/** A single stat row inside a perf card. */\nfunction Stat({ label, value }: { label: string; value: string }): React.ReactElement {\n\treturn (\n\t\t<div className=\"perf-metric\">\n\t\t\t<span className=\"perf-label\">{label}</span>\n\t\t\t<span className=\"perf-value\">{value}</span>\n\t\t</div>\n\t);\n}\n\n/** Card wrapper with a title. */\nfunction Card({ title, children }: { title: string; children: React.ReactNode }): React.ReactElement {\n\treturn (\n\t\t<div className=\"perf-card\">\n\t\t\t<h3 className=\"perf-card-title\">{title}</h3>\n\t\t\t<div className=\"perf-card-body\">{children}</div>\n\t\t</div>\n\t);\n}\n\nexport function PerfCards({ perf }: PerfCardsProps): React.ReactElement {\n\tconst cpuTxt = `${fmtNum(perf.cpu_user_ms.latest, 1)} / ${fmtNum(perf.cpu_sys_ms.latest, 1)} ms`;\n\n\treturn (\n\t\t<div className=\"perf-cards-grid\">\n\t\t\t<Card title=\"Model latency\">\n\t\t\t\t<Stat label=\"Turn p50\" value={fmtMs(perf.turn_latency_ms.p50)} />\n\t\t\t\t<Stat label=\"Turn p95\" value={fmtMs(perf.turn_latency_ms.p95)} />\n\t\t\t\t<Stat label=\"Provider p50\" value={fmtMs(perf.provider_latency_ms.p50)} />\n\t\t\t\t<Stat label=\"Provider p95\" value={fmtMs(perf.provider_latency_ms.p95)} />\n\t\t\t</Card>\n\n\t\t\t<Card title=\"Throughput\">\n\t\t\t\t<Stat label=\"TPS (avg)\" value={fmtNum(perf.tps.avg, 1)} />\n\t\t\t\t<Stat\n\t\t\t\t\tlabel=\"Cache hit %\"\n\t\t\t\t\tvalue={`${fmtNum(perf.cache_hit_pct.avg, 1)}%`}\n\t\t\t\t/>\n\t\t\t</Card>\n\n\t\t\t<Card title=\"Process\">\n\t\t\t\t<Stat label=\"RSS\" value={`${fmtNum(perf.rss_mb.latest, 1)} MB`} />\n\t\t\t\t<Stat label=\"Heap\" value={`${fmtNum(perf.heap_mb.latest, 1)} MB`} />\n\t\t\t\t<Stat label=\"CPU user / sys\" value={cpuTxt} />\n\t\t\t</Card>\n\n\t\t\t<Card title=\"Snapshot cost\">\n\t\t\t\t<Stat label=\"DB recompute p50\" value={fmtMs(perf.db_recompute_ms.p50)} />\n\t\t\t\t<Stat label=\"DB recompute p95\" value={fmtMs(perf.db_recompute_ms.p95)} />\n\t\t\t\t<Stat label=\"Disk write p50\" value={fmtMs(perf.disk_write_ms.p50)} />\n\t\t\t</Card>\n\n\t\t\t<Card title=\"TUI lag proxy\">\n\t\t\t\t<Stat label=\"Live-trim fires\" value={fmtDiag(perf.diag?.liveTrimFires)} />\n\t\t\t\t<Stat label=\"Cache replays\" value={fmtDiag(perf.diag?.liveTrimReplays)} />\n\t\t\t\t<Stat label=\"Fast-gate skips\" value={fmtDiag(perf.diag?.ctxFastGate)} />\n\t\t\t\t<span className=\"perf-card-note\">skip vs recompute vs replay cadence</span>\n\t\t\t</Card>\n\t\t</div>\n\t);\n}\n","/**\n * dashboard-client/src/components/ModelBadge.tsx — model + provider + rates.\n *\n * Shows the active model name, provider, and input/output rates.\n * Tooltips copied verbatim from html.ts. Used in the Metrics tab header.\n */\n\nimport type React from \"react\";\n\nexport interface ModelBadgeProps {\n\t/** Model name/identifier. */\n\tname: string;\n\t/** Human-readable provider name. */\n\tproviderName: string;\n\t/** Machine-readable provider identifier. */\n\tprovider: string;\n\t/** USD per input token (from model pricing). */\n\tinputRate: number;\n\t/** USD per output token (from model pricing). */\n\toutputRate: number;\n}\n\nfunction Field({\n\tlabel,\n\tvalue,\n\ttitle,\n}: {\n\tlabel: string;\n\tvalue: string;\n\ttitle?: string;\n}): React.ReactElement {\n\treturn (\n\t\t<div className=\"model-field\" title={title}>\n\t\t\t<span className=\"model-field-label\">{label}</span>\n\t\t\t<span className=\"model-field-value\">{value}</span>\n\t\t</div>\n\t);\n}\n\nexport function ModelBadge({\n\tname,\n\tproviderName,\n\tprovider,\n\tinputRate,\n\toutputRate,\n}: ModelBadgeProps): React.ReactElement {\n\tconst providerDisplay = providerName || provider || \"—\";\n\treturn (\n\t\t<div className=\"model-badge\">\n\t\t\t<Field\n\t\t\t\tlabel=\"Model\"\n\t\t\t\tvalue={name}\n\t\t\t\ttitle=\"The model pi is currently using — its pricing drives the cost figure.\"\n\t\t\t/>\n\t\t\t<Field\n\t\t\t\tlabel=\"Provider\"\n\t\t\t\tvalue={providerDisplay}\n\t\t\t\ttitle=\"The provider serving the model.\"\n\t\t\t/>\n\t\t\t<Field\n\t\t\t\tlabel=\"Input Rate\"\n\t\t\t\tvalue={`$${inputRate.toFixed(6)}`}\n\t\t\t\ttitle=\"USD per input token, from the model's pricing.\"\n\t\t\t/>\n\t\t\t<Field\n\t\t\t\tlabel=\"Output Rate\"\n\t\t\t\tvalue={`$${outputRate.toFixed(6)}`}\n\t\t\t\ttitle=\"USD per output token, from the model's pricing.\"\n\t\t\t/>\n\t\t</div>\n\t);\n}\n","/**\n * dashboard-client/src/tabs/MetricsTab.tsx — Metrics tab (C2).\n *\n * Fetches /api/perf (rolling-window aggregates) + /api/snapshot (for model).\n * Renders ModelBadge + PerfChart + 5 perf cards (latency, throughput, process,\n * snapshot cost, TUI lag proxy). Polls every 10s.\n */\n\nimport type React from \"react\";\nimport { useCallback } from \"react\";\nimport { useApi } from \"../hooks/useApi\";\nimport { fetchPerf, fetchSnapshot } from \"../api/client\";\nimport type { PerfResponse, SnapshotResponse } from \"@contracts\";\nimport { PerfChart } from \"../components/PerfChart\";\nimport { PerfCards } from \"../components/PerfCards\";\nimport { ModelBadge } from \"../components/ModelBadge\";\n\nexport default function MetricsTab(): React.ReactElement {\n\tconst { data: perf, error: perfErr } = useApi<PerfResponse>(\n\t\tuseCallback(() => fetchPerf({ minutes: 30 }), []),\n\t\t{ pollInterval: 10_000 },\n\t);\n\tconst { data: snapshot } = useApi<SnapshotResponse>(\n\t\tuseCallback(() => fetchSnapshot(), []),\n\t\t{ pollInterval: 10_000 },\n\t);\n\n\tif (perfErr && !perf) {\n\t\treturn (\n\t\t\t<div className=\"tab-stub\">Error loading perf: {perfErr.message}</div>\n\t\t);\n\t}\n\tif (!perf) {\n\t\treturn <div className=\"tab-stub\">Loading perf…</div>;\n\t}\n\n\treturn (\n\t\t<div className=\"metrics-tab\">\n\t\t\t{snapshot?.model && (\n\t\t\t\t<ModelBadge\n\t\t\t\t\tname={snapshot.model.name}\n\t\t\t\t\tproviderName={snapshot.model.providerName}\n\t\t\t\t\tprovider={snapshot.model.provider}\n\t\t\t\t\tinputRate={snapshot.model.inputRate}\n\t\t\t\t\toutputRate={snapshot.model.outputRate}\n\t\t\t\t/>\n\t\t\t)}\n\t\t\t<PerfChart perf={perf} />\n\t\t\t<PerfCards perf={perf} />\n\t\t\t<div className=\"perf-footer\">\n\t\t\t\t{perf.sampleCount} samples · updated {perf.updatedAt}\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n"],"names":["fmtMs","n","Bar","value","max","colorClass","pct","jsx","PerfChart","perf","latencyMax","tpsRef","jsxs","v","fmtNum","dec","fmtDiag","Stat","label","Card","title","children","PerfCards","cpuTxt","Field","ModelBadge","name","providerName","provider","inputRate","outputRate","providerDisplay","MetricsTab","perfErr","useApi","useCallback","fetchPerf","snapshot","fetchSnapshot"],"mappings":"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"}
1
+ {"version":3,"file":"MetricsTab-Cq4u-rZF.js","sources":["../../src/components/PerfChart.tsx","../../src/components/PerfCards.tsx","../../src/components/ModelBadge.tsx","../../src/tabs/MetricsTab.tsx"],"sourcesContent":["/**\n * dashboard-client/src/components/PerfChart.tsx — perf stat chart.\n *\n * The /api/perf endpoint returns rolling-window aggregates (not time-series),\n * so this renders a stat-card \"chart\": p50/p95 latency bars, TPS, cache hit %,\n * db recompute + disk write. Each metric shows a normalized bar + numeric.\n */\n\nimport type React from \"react\";\nimport type { PerfResponse } from \"@contracts\";\n\nexport interface PerfChartProps {\n\tperf: PerfResponse;\n}\n\nfunction fmtMs(n: number): string {\n\treturn n >= 1000 ? `${(n / 1000).toFixed(1)}s` : `${n.toFixed(0)}ms`;\n}\n\ninterface BarProps {\n\t/** Current value. */\n\tvalue: number;\n\t/** Value that represents 100% bar fill (the \"max\" reference). */\n\tmax: number;\n\t/** Color class. */\n\tcolorClass: string;\n}\n\nfunction Bar({ value, max, colorClass }: BarProps): React.ReactElement {\n\tconst pct = max > 0 ? Math.max(0, Math.min(100, (value / max) * 100)) : 0;\n\treturn (\n\t\t<div className=\"perf-bar-track\">\n\t\t\t<div\n\t\t\t\tclassName={`perf-bar-fill ${colorClass}`}\n\t\t\t\tstyle={{ width: `${pct}%` }}\n\t\t\t/>\n\t\t</div>\n\t);\n}\n\nexport function PerfChart({ perf }: PerfChartProps): React.ReactElement {\n\t// Reference maxes: p95 is the upper bound for latency bars; rates scale to a\n\t// reasonable reference (tok/s) so bars are visually meaningful even with\n\t// small samples. cache_hit_pct is already 0-100.\n\tconst latencyMax = Math.max(\n\t\tperf.turn_latency_ms.p95,\n\t\tperf.provider_latency_ms.p95,\n\t\t1,\n\t);\n\tconst tpsRef = Math.max(perf.tps.avg, 1);\n\n\treturn (\n\t\t<div className=\"perf-chart\">\n\t\t\t<div className=\"perf-header\">\n\t\t\t\t<h3>Performance</h3>\n\t\t\t\t<span className=\"perf-window\">\n\t\t\t\t\t{perf.windowMinutes}min window · {perf.sampleCount} samples\n\t\t\t\t</span>\n\t\t\t</div>\n\t\t\t<div className=\"perf-grid\">\n\t\t\t\t<div className=\"perf-metric\">\n\t\t\t\t\t<span className=\"perf-label\">Turn latency p50 / p95</span>\n\t\t\t\t\t<span className=\"perf-value\">\n\t\t\t\t\t\t{fmtMs(perf.turn_latency_ms.p50)} /{\" \"}\n\t\t\t\t\t\t{fmtMs(perf.turn_latency_ms.p95)}\n\t\t\t\t\t</span>\n\t\t\t\t\t<Bar\n\t\t\t\t\t\tvalue={perf.turn_latency_ms.p95}\n\t\t\t\t\t\tmax={latencyMax}\n\t\t\t\t\t\tcolorClass=\"perf-blue\"\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"perf-metric\">\n\t\t\t\t\t<span className=\"perf-label\">Provider latency p50 / p95</span>\n\t\t\t\t\t<span className=\"perf-value\">\n\t\t\t\t\t\t{fmtMs(perf.provider_latency_ms.p50)} /{\" \"}\n\t\t\t\t\t\t{fmtMs(perf.provider_latency_ms.p95)}\n\t\t\t\t\t</span>\n\t\t\t\t\t<Bar\n\t\t\t\t\t\tvalue={perf.provider_latency_ms.p95}\n\t\t\t\t\t\tmax={latencyMax}\n\t\t\t\t\t\tcolorClass=\"perf-purple\"\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"perf-metric\">\n\t\t\t\t\t<span className=\"perf-label\">Tokens/sec (avg)</span>\n\t\t\t\t\t<span className=\"perf-value\">{perf.tps.avg.toFixed(1)}</span>\n\t\t\t\t\t<Bar value={perf.tps.avg} max={tpsRef} colorClass=\"perf-green\" />\n\t\t\t\t</div>\n\t\t\t\t<div className=\"perf-metric\">\n\t\t\t\t\t<span className=\"perf-label\">Cache hit (avg / latest)</span>\n\t\t\t\t\t<span className=\"perf-value\">\n\t\t\t\t\t\t{perf.cache_hit_pct.avg.toFixed(1)}% /{\" \"}\n\t\t\t\t\t\t{perf.cache_hit_pct.latest.toFixed(1)}%\n\t\t\t\t\t</span>\n\t\t\t\t\t<Bar\n\t\t\t\t\t\tvalue={perf.cache_hit_pct.avg}\n\t\t\t\t\t\tmax={100}\n\t\t\t\t\t\tcolorClass=\"perf-green\"\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"perf-metric\">\n\t\t\t\t\t<span className=\"perf-label\">DB recompute p95</span>\n\t\t\t\t\t<span className=\"perf-value\">{fmtMs(perf.db_recompute_ms.p95)}</span>\n\t\t\t\t\t<Bar\n\t\t\t\t\t\tvalue={perf.db_recompute_ms.p95}\n\t\t\t\t\t\tmax={latencyMax}\n\t\t\t\t\t\tcolorClass=\"perf-yellow\"\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"perf-metric\">\n\t\t\t\t\t<span className=\"perf-label\">Disk write p95</span>\n\t\t\t\t\t<span className=\"perf-value\">{fmtMs(perf.disk_write_ms.p95)}</span>\n\t\t\t\t\t<Bar\n\t\t\t\t\t\tvalue={perf.disk_write_ms.p95}\n\t\t\t\t\t\tmax={latencyMax}\n\t\t\t\t\t\tcolorClass=\"perf-yellow\"\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<div className=\"perf-resources\">\n\t\t\t\t<div className=\"perf-metric\">\n\t\t\t\t\t<span className=\"perf-label\">RSS</span>\n\t\t\t\t\t<span className=\"perf-value\">{perf.rss_mb.latest.toFixed(0)} MB</span>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"perf-metric\">\n\t\t\t\t\t<span className=\"perf-label\">Heap</span>\n\t\t\t\t\t<span className=\"perf-value\">\n\t\t\t\t\t\t{perf.heap_mb.latest.toFixed(0)} MB\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"perf-metric\">\n\t\t\t\t\t<span className=\"perf-label\">CPU user</span>\n\t\t\t\t\t<span className=\"perf-value\">\n\t\t\t\t\t\t{(perf.cpu_user_ms.latest / 1000).toFixed(1)}s\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"perf-metric\">\n\t\t\t\t\t<span className=\"perf-label\">CPU sys</span>\n\t\t\t\t\t<span className=\"perf-value\">\n\t\t\t\t\t\t{(perf.cpu_sys_ms.latest / 1000).toFixed(1)}s\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t{perf.diag && (\n\t\t\t\t<div className=\"perf-diag\">\n\t\t\t\t\t<span className=\"perf-label\">\n\t\t\t\t\t\tFast-gate fires: {perf.diag.ctxFastGate}\n\t\t\t\t\t</span>\n\t\t\t\t\t<span className=\"perf-label\">\n\t\t\t\t\t\tLive trim fires: {perf.diag.liveTrimFires}\n\t\t\t\t\t</span>\n\t\t\t\t\t<span className=\"perf-label\">\n\t\t\t\t\t\tLive trim replays: {perf.diag.liveTrimReplays}\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n","/**\n * dashboard-client/src/components/PerfCards.tsx — 5 perf metric cards.\n *\n * Mirrors the old html.ts perf grid: model latency, throughput, process,\n * snapshot cost, and TUI lag proxy. Uses the existing perf-metric CSS\n * classes from repos-metrics.css plus metrics-extra.css for card headers.\n */\n\nimport type React from \"react\";\nimport type { PerfResponse } from \"@contracts\";\n\n/** Format milliseconds (em-dash for null/undefined). */\nfunction fmtMs(v: number | null | undefined): string {\n\treturn v == null ? \"\\u2014\" : v >= 100 ? `${Math.round(v)}ms` : `${v.toFixed(1)}ms`;\n}\n\n/** Format a number with fixed decimals (em-dash for null/non-number). */\nfunction fmtNum(v: number | null | undefined, dec: number): string {\n\treturn v == null || typeof v !== \"number\" ? \"\\u2014\" : v.toFixed(dec);\n}\n\n/** Format a diag counter (em-dash for null). */\nfunction fmtDiag(v: number | null | undefined): string {\n\treturn v == null ? \"\\u2014\" : String(v);\n}\n\ninterface PerfCardsProps {\n\tperf: PerfResponse;\n}\n\n/** A single stat row inside a perf card. */\nfunction Stat({ label, value }: { label: string; value: string }): React.ReactElement {\n\treturn (\n\t\t<div className=\"perf-metric\">\n\t\t\t<span className=\"perf-label\">{label}</span>\n\t\t\t<span className=\"perf-value\">{value}</span>\n\t\t</div>\n\t);\n}\n\n/** Card wrapper with a title. */\nfunction Card({ title, children }: { title: string; children: React.ReactNode }): React.ReactElement {\n\treturn (\n\t\t<div className=\"perf-card\">\n\t\t\t<h3 className=\"perf-card-title\">{title}</h3>\n\t\t\t<div className=\"perf-card-body\">{children}</div>\n\t\t</div>\n\t);\n}\n\nexport function PerfCards({ perf }: PerfCardsProps): React.ReactElement {\n\tconst cpuTxt = `${fmtNum(perf.cpu_user_ms.latest, 1)} / ${fmtNum(perf.cpu_sys_ms.latest, 1)} ms`;\n\n\treturn (\n\t\t<div className=\"perf-cards-grid\">\n\t\t\t<Card title=\"Model latency\">\n\t\t\t\t<Stat label=\"Turn p50\" value={fmtMs(perf.turn_latency_ms.p50)} />\n\t\t\t\t<Stat label=\"Turn p95\" value={fmtMs(perf.turn_latency_ms.p95)} />\n\t\t\t\t<Stat label=\"Provider p50\" value={fmtMs(perf.provider_latency_ms.p50)} />\n\t\t\t\t<Stat label=\"Provider p95\" value={fmtMs(perf.provider_latency_ms.p95)} />\n\t\t\t</Card>\n\n\t\t\t<Card title=\"Throughput\">\n\t\t\t\t<Stat label=\"TPS (avg)\" value={fmtNum(perf.tps.avg, 1)} />\n\t\t\t\t<Stat\n\t\t\t\t\tlabel=\"Cache hit %\"\n\t\t\t\t\tvalue={`${fmtNum(perf.cache_hit_pct.avg, 1)}%`}\n\t\t\t\t/>\n\t\t\t</Card>\n\n\t\t\t<Card title=\"Process\">\n\t\t\t\t<Stat label=\"RSS\" value={`${fmtNum(perf.rss_mb.latest, 1)} MB`} />\n\t\t\t\t<Stat label=\"Heap\" value={`${fmtNum(perf.heap_mb.latest, 1)} MB`} />\n\t\t\t\t<Stat label=\"CPU user / sys\" value={cpuTxt} />\n\t\t\t</Card>\n\n\t\t\t<Card title=\"Snapshot cost\">\n\t\t\t\t<Stat label=\"DB recompute p50\" value={fmtMs(perf.db_recompute_ms.p50)} />\n\t\t\t\t<Stat label=\"DB recompute p95\" value={fmtMs(perf.db_recompute_ms.p95)} />\n\t\t\t\t<Stat label=\"Disk write p50\" value={fmtMs(perf.disk_write_ms.p50)} />\n\t\t\t</Card>\n\n\t\t\t<Card title=\"TUI lag proxy\">\n\t\t\t\t<Stat label=\"Live-trim fires\" value={fmtDiag(perf.diag?.liveTrimFires)} />\n\t\t\t\t<Stat label=\"Cache replays\" value={fmtDiag(perf.diag?.liveTrimReplays)} />\n\t\t\t\t<Stat label=\"Fast-gate skips\" value={fmtDiag(perf.diag?.ctxFastGate)} />\n\t\t\t\t<span className=\"perf-card-note\">skip vs recompute vs replay cadence</span>\n\t\t\t</Card>\n\t\t</div>\n\t);\n}\n","/**\n * dashboard-client/src/components/ModelBadge.tsx — model + provider + rates.\n *\n * Shows the active model name, provider, and input/output rates.\n * Tooltips copied verbatim from html.ts. Used in the Metrics tab header.\n */\n\nimport type React from \"react\";\n\nexport interface ModelBadgeProps {\n\t/** Model name/identifier. */\n\tname: string;\n\t/** Human-readable provider name. */\n\tproviderName: string;\n\t/** Machine-readable provider identifier. */\n\tprovider: string;\n\t/** USD per input token (from model pricing). */\n\tinputRate: number;\n\t/** USD per output token (from model pricing). */\n\toutputRate: number;\n}\n\nfunction Field({\n\tlabel,\n\tvalue,\n\ttitle,\n}: {\n\tlabel: string;\n\tvalue: string;\n\ttitle?: string;\n}): React.ReactElement {\n\treturn (\n\t\t<div className=\"model-field\" title={title}>\n\t\t\t<span className=\"model-field-label\">{label}</span>\n\t\t\t<span className=\"model-field-value\">{value}</span>\n\t\t</div>\n\t);\n}\n\nexport function ModelBadge({\n\tname,\n\tproviderName,\n\tprovider,\n\tinputRate,\n\toutputRate,\n}: ModelBadgeProps): React.ReactElement {\n\tconst providerDisplay = providerName || provider || \"—\";\n\treturn (\n\t\t<div className=\"model-badge\">\n\t\t\t<Field\n\t\t\t\tlabel=\"Model\"\n\t\t\t\tvalue={name}\n\t\t\t\ttitle=\"The model pi is currently using — its pricing drives the cost figure.\"\n\t\t\t/>\n\t\t\t<Field\n\t\t\t\tlabel=\"Provider\"\n\t\t\t\tvalue={providerDisplay}\n\t\t\t\ttitle=\"The provider serving the model.\"\n\t\t\t/>\n\t\t\t<Field\n\t\t\t\tlabel=\"Input Rate\"\n\t\t\t\tvalue={`$${inputRate.toFixed(6)}`}\n\t\t\t\ttitle=\"USD per input token, from the model's pricing.\"\n\t\t\t/>\n\t\t\t<Field\n\t\t\t\tlabel=\"Output Rate\"\n\t\t\t\tvalue={`$${outputRate.toFixed(6)}`}\n\t\t\t\ttitle=\"USD per output token, from the model's pricing.\"\n\t\t\t/>\n\t\t</div>\n\t);\n}\n","/**\n * dashboard-client/src/tabs/MetricsTab.tsx — Metrics tab (C2).\n *\n * Fetches /api/perf (rolling-window aggregates) + /api/snapshot (for model).\n * Renders ModelBadge + PerfChart + 5 perf cards (latency, throughput, process,\n * snapshot cost, TUI lag proxy). Polls every 10s.\n */\n\nimport type React from \"react\";\nimport { useCallback } from \"react\";\nimport { useApi } from \"../hooks/useApi\";\nimport { fetchPerf, fetchSnapshot } from \"../api/client\";\nimport type { PerfResponse, SnapshotResponse } from \"@contracts\";\nimport { PerfChart } from \"../components/PerfChart\";\nimport { PerfCards } from \"../components/PerfCards\";\nimport { ModelBadge } from \"../components/ModelBadge\";\n\nexport default function MetricsTab(): React.ReactElement {\n\tconst { data: perf, error: perfErr } = useApi<PerfResponse>(\n\t\tuseCallback(() => fetchPerf({ minutes: 30 }), []),\n\t\t{ pollInterval: 10_000 },\n\t);\n\tconst { data: snapshot } = useApi<SnapshotResponse>(\n\t\tuseCallback(() => fetchSnapshot(), []),\n\t\t{ pollInterval: 10_000 },\n\t);\n\n\tif (perfErr && !perf) {\n\t\treturn (\n\t\t\t<div className=\"tab-stub\">Error loading perf: {perfErr.message}</div>\n\t\t);\n\t}\n\tif (!perf) {\n\t\treturn <div className=\"tab-stub\">Loading perf…</div>;\n\t}\n\n\treturn (\n\t\t<div className=\"metrics-tab\">\n\t\t\t{snapshot?.model && (\n\t\t\t\t<ModelBadge\n\t\t\t\t\tname={snapshot.model.name}\n\t\t\t\t\tproviderName={snapshot.model.providerName}\n\t\t\t\t\tprovider={snapshot.model.provider}\n\t\t\t\t\tinputRate={snapshot.model.inputRate}\n\t\t\t\t\toutputRate={snapshot.model.outputRate}\n\t\t\t\t/>\n\t\t\t)}\n\t\t\t<PerfChart perf={perf} />\n\t\t\t<PerfCards perf={perf} />\n\t\t\t<div className=\"perf-footer\">\n\t\t\t\t{perf.sampleCount} samples · updated {perf.updatedAt}\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n"],"names":["fmtMs","n","Bar","value","max","colorClass","pct","jsx","PerfChart","perf","latencyMax","tpsRef","jsxs","v","fmtNum","dec","fmtDiag","Stat","label","Card","title","children","PerfCards","cpuTxt","Field","ModelBadge","name","providerName","provider","inputRate","outputRate","providerDisplay","MetricsTab","perfErr","useApi","useCallback","fetchPerf","snapshot","fetchSnapshot"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import{j as e,u as H,r as C,f as I}from"./index-BcoSz-L4.js";import{u as A}from"./useSSE-Cezlih5V.js";import{f as g,a as E,C as _,T as G}from"./TimeSavedCard-0TqsrY6y.js";const $=["#58a6ff","#3fb950","#d29922","#f85149","#bc8cff","#ff7b72","#79c0ff","#56d4dd","#e3b341","#a5d6ff","#7ee787","#ffa657"];function B(s){let t=5381;for(let a=0;a<s.length;a++)t=(t<<5)+t+s.charCodeAt(a)>>>0;return t}function U(s){return s?$[B(s)%$.length]:$[0]}function K(s){if(!s)return"(unknown)";const t=s.replace(/[\\/]+$/,"");if(!t)return"(unknown)";const a=t.split(/[\\/]/);return a[a.length-1]||t}function z(s){const t=new Map;for(const o of s){const i=o.repoRoot??"__null__",u=o.tokens??0,m=t.get(i);if(m)m.tokens+=u;else{const c=K(o.repoRoot);t.set(i,{repoRoot:o.repoRoot,basename:c,tokens:u,color:U(c)})}}const a=[...t.values()],r=a.reduce((o,i)=>o+i.tokens,0),l=s.reduce((o,i)=>Math.max(o,i.ctxWindow),0);return{repos:a,totalTokens:r,maxWindow:l,sessionCount:s.length}}function R(s){return s>=1e5?`${(s/1e3).toFixed(0)}k`:s.toLocaleString()}function V({sessions:s,loading:t,error:a}){if(a)return e.jsxs("div",{className:"card repo-stack-card",children:[e.jsx("h3",{children:"Context per Repo (live)"}),e.jsxs("div",{className:"sessions-empty",children:["Error loading sessions: ",a.message]})]});if(t&&!s)return e.jsxs("div",{className:"card repo-stack-card",children:[e.jsx("h3",{children:"Context per Repo (live)"}),e.jsx("div",{className:"sessions-empty",children:"Loading sessions…"})]});const r=s?.sessions??[],{repos:l,totalTokens:o,maxWindow:i,sessionCount:u}=z(r);if(l.length===0)return e.jsxs("div",{className:"card repo-stack-card",children:[e.jsx("h3",{children:"Context per Repo (live)"}),e.jsx("div",{className:"sessions-empty",children:"No active sessions."})]});const m=i>0?Math.round(o/i*100):0;return e.jsxs("div",{className:"card repo-stack-card",children:[e.jsx("h3",{children:"Context per Repo (live)"}),e.jsx("div",{className:"repo-stack-bar",role:"group","aria-label":"Per-repo context usage",children:l.map(c=>{const n=o>0?c.tokens/o*100:0,h=o>0?n:0,d=`${c.basename}: ${R(c.tokens)} tokens (${n.toFixed(1)}% of total)`;return e.jsx("div",{className:"repo-stack-segment",style:{width:`${Math.max(h,.5)}%`,backgroundColor:c.color},title:d,"aria-label":d},c.repoRoot??"__null__")})}),e.jsxs("p",{className:"repo-stack-total",children:[R(o)," tokens across ",l.length," ",l.length===1?"repo":"repos"," /"," ",u," ",u===1?"session":"sessions"," ",e.jsxs("span",{className:"pct",children:["— ",m,"% of combined max window"]})]}),e.jsx("div",{className:"repo-stack-legend",children:l.map(c=>{const n=o>0?c.tokens/o*100:0;return e.jsxs("div",{className:"repo-stack-legend-item",children:[e.jsx("span",{className:"repo-stack-legend-swatch",style:{backgroundColor:c.color}}),e.jsx("span",{className:"repo-stack-legend-name",children:c.basename}),e.jsxs("span",{children:[R(c.tokens)," (",n.toFixed(0),"%)"]})]},c.repoRoot??"__null__")})})]})}function q(s){return s>=90?"gauge-red":s>=70?"gauge-yellow":"gauge-green"}function X(s){return s==null?"?":s>=1e5?`${(s/1e3).toFixed(0)}k`:s.toLocaleString()}function J(s){if(!s)return"(unknown)";const t=s.replace(/[\\/]+$/,"");if(!t)return"(unknown)";const a=t.split(/[\\/]/);return a[a.length-1]||t}function Q(s){const t=Math.max(0,Math.round(s/1e3));if(t<60)return`${t}s ago`;const a=Math.floor(t/60);return a<60?`${a}m ago`:`${Math.floor(a/60)}h ${a%60}m ago`}function W({data:s}){const t=s.percent??0,a=Math.max(t,1),r=q(t),l=X(s.tokens),o=s.contextWindow.toLocaleString(),i=`${l} / ${o} tokens`;return e.jsxs("div",{className:`session-gauge ${s.isSelf?"session-gauge-self":""}`,children:[e.jsxs("div",{className:"session-gauge-head",children:[e.jsx("span",{className:"session-gauge-label",children:s.label}),e.jsxs("span",{className:"session-gauge-pct",children:[t,"%"]})]}),e.jsx("div",{className:`gauge-bar ${r}`,role:"meter","aria-valuenow":t,"aria-valuemin":0,"aria-valuemax":100,"aria-label":`${s.label}: ${t}%`,children:e.jsx("div",{className:"gauge-fill",style:{width:`${a}%`}})}),e.jsx("p",{className:"gauge-label session-gauge-sub",children:i}),s.sublabel&&e.jsx("p",{className:"session-gauge-meta",children:s.sublabel})]})}function Y({sessions:s,loading:t,error:a,launcherSessionId:r,launcher:l}){const o=Date.now(),i=[{key:"self",label:"this session",sublabel:"",tokens:l.tokens,percent:l.percent,contextWindow:l.contextWindow,isSelf:!0}],u=(s?.sessions??[]).filter(n=>n.sessionId!==r);u.sort((n,h)=>h.lastSeen-n.lastSeen);for(const n of u)i.push({key:n.sessionId,label:J(n.repoRoot),sublabel:`pid ${n.pid} · ${Q(o-n.lastSeen)}${n.model?` · ${n.model}`:""}`,tokens:n.tokens,percent:n.percent,contextWindow:n.ctxWindow||0,isSelf:!1});const m=a&&!s,c=t&&!s;return e.jsxs("div",{className:"card session-gauges-card",children:[e.jsx("h3",{children:"Context per Session (live)"}),m?e.jsxs("div",{className:"sessions-empty",children:["Error loading sessions: ",a.message]}):c?e.jsx("div",{className:"sessions-empty",children:"Loading sessions…"}):i.length<=1&&u.length===0?e.jsx("div",{className:"session-gauges-grid",children:i.map(n=>e.jsx(W,{data:n},n.key))}):e.jsx("div",{className:"session-gauges-grid",children:i.map(n=>e.jsx(W,{data:n},n.key))})]})}function M({state:s,label:t}){const a=s==="on"?"bullet-on":s==="na"?"bullet-na":"bullet-off";return e.jsxs("div",{className:`bullet ${a}`,children:[e.jsx("span",{className:"bullet-dot","aria-hidden":"true"}),e.jsx("span",{className:"bullet-label",children:t})]})}function Z({armed:s,ready:t}){const a=s?"on":"off",r=t?"on":s?"off":"na",l=t?"THRESHOLD EXCEEDED — compacting next event":s?"past fast gate — monitoring token count":"idle — below fast gate";return e.jsxs("div",{className:"card trigger-status",children:[e.jsx("h3",{children:"Trigger Status"}),e.jsxs("div",{className:"bullet-row",children:[e.jsx(M,{state:a,label:"Armed"}),e.jsx(M,{state:r,label:"Ready"})]}),e.jsx("p",{className:"trigger-state-text",children:l})]})}function ee(s){return s>=.9?"meter-green":s>=.6?"meter-yellow":"meter-red"}function p({label:s,value:t,title:a}){return e.jsxs("div",{className:"ov-stat-row",children:[e.jsx("span",{className:"ov-stat-label",title:a,children:s}),e.jsx("span",{className:"ov-stat-value",children:t})]})}function se(s){const t=s.compressionPct,a=ee(t),r=Math.max(t*100,.5),l=`${g(t)} tokens saved · dedup: ${g(s.dedupPct)}`;return e.jsxs("div",{className:"card vector-store-card",children:[e.jsx("h3",{children:"Vector Store (session)"}),e.jsx(p,{label:"Checkpoints",value:s.checkpointCount.toLocaleString(),title:"A saved summary of a chunk of your conversation that was compacted to free up space."}),e.jsx(p,{label:"Original (dropped)",value:s.tokensIn.toLocaleString(),title:"Total size of the original conversation text dropped into compaction this session, including redundant regions skipped by dedup. This is the 'in'."}),e.jsx(p,{label:"Kept (summaries)",value:s.tokensOut.toLocaleString(),title:"Compact summaries we are currently holding as 'memory' for this session (the 'out'). Smaller is better."}),e.jsx(p,{label:"Freed",value:s.tokensFreed.toLocaleString(),title:"Conversation space freed = dropped − kept (the 'saved')."}),e.jsx(p,{label:"Injected",value:s.injectedCount,title:"How many times old context was automatically brought back into the conversation because it was relevant to what you were doing."}),e.jsx(p,{label:"Recall Relevance",value:g(s.dedupHitRate),title:"Of the times we recalled old context, how often it was actually on-topic."}),e.jsx(p,{label:"Storage Dedup",value:g(s.storageDedupRate),title:"How often new content matched something we already had, so we skipped storing a duplicate copy. Higher = less wasted space."}),e.jsx(p,{label:"Collapsed",value:s.dedupCollapsed,title:"How many duplicate chunks we collapsed into one instead of storing separately."}),e.jsx(p,{label:"Last ID",value:s.lastCheckpointId??"—",title:"The ID of the most recent saved checkpoint."}),e.jsxs("div",{className:"ov-compression-meter",children:[e.jsx("div",{className:"meter-track",children:e.jsx("div",{className:`meter-fill ${a}`,style:{width:`${r}%`}})}),e.jsx("span",{className:"meter-sub",children:l})]})]})}function te(s){return s>=.9?"meter-green":s>=.6?"meter-yellow":"meter-red"}function x({label:s,value:t}){return e.jsxs("div",{className:"ov-stat-row",children:[e.jsx("span",{className:"ov-stat-label",children:s}),e.jsx("span",{className:"ov-stat-value",children:t})]})}function ae(s){const t=s.compressionPct,a=te(t),r=Math.max(t*100,.5),l=`${g(t)} tokens saved · dedup: ${g(s.dedupPct)}`;return e.jsxs("div",{className:"card repo-sessions-card",children:[e.jsx("h3",{children:"Repo (all sessions)"}),e.jsx(x,{label:"Checkpoints",value:s.checkpointCount.toLocaleString()}),e.jsx(x,{label:"Original",value:s.tokensIn.toLocaleString()}),e.jsx(x,{label:"Kept",value:s.tokensOut.toLocaleString()}),e.jsx(x,{label:"Freed",value:s.tokensFreed.toLocaleString()}),e.jsx(x,{label:"Sessions",value:s.sessionCount.toLocaleString()}),e.jsx(x,{label:"Collapsed",value:s.dedupCollapsed}),e.jsx(x,{label:"Storage Dedup",value:g(s.storageDedupRate)}),e.jsxs("div",{className:"ov-compression-meter",children:[e.jsx("div",{className:"meter-track",children:e.jsx("div",{className:`meter-fill ${a}`,style:{width:`${r}%`}})}),e.jsx("span",{className:"meter-sub",children:l})]})]})}function ne(s){return e.jsxs("div",{className:"card data-safety-card",children:[e.jsx("h3",{children:"🛡 Data Safety"}),e.jsxs("div",{className:"ov-stat-row",children:[e.jsx("span",{className:"ov-stat-label",children:"Regions Retained"}),e.jsx("span",{className:"ov-stat-value",children:s.regionsRetained.toLocaleString()})]}),e.jsxs("div",{className:"ov-stat-row",children:[e.jsx("span",{className:"ov-stat-label",children:"Compressed-Original"}),e.jsx("span",{className:"ov-stat-value",children:E(s.compressedOriginalBytes)})]}),e.jsxs("div",{className:"ov-stat-row",children:[e.jsx("span",{className:"ov-stat-label",children:"Dedup Duplicates"}),e.jsx("span",{className:"ov-stat-value",children:s.duplicatesCollapsed.toLocaleString()})]}),e.jsxs("div",{className:"ov-stat-row",children:[e.jsx("span",{className:"ov-stat-label",children:"Permanently Deleted"}),e.jsx("span",{className:"ov-stat-value ov-value-ok",children:E(s.bytesPermanentlyDeleted)})]}),e.jsx("p",{className:"safe-note",children:'Every compacted region is kept verbatim (compressed). "Drop" = removed from the live window only. We never delete your data.'})]})}function v({label:s,value:t,title:a}){return e.jsxs("div",{className:"ov-stat-row",children:[e.jsx("span",{className:"ov-stat-label",title:a,children:s}),e.jsx("span",{className:"ov-stat-value",children:t})]})}function oe(s){const t=`${Math.round((s.pressure??0)*100)}%`;let a=s.thresholdTokens.toLocaleString();return s.tierPct!=null&&s.contextWindow>0&&(a+=` (${Math.round(s.tierPct*100)}% of ${s.contextWindow.toLocaleString()})`),e.jsxs("div",{className:"card config-summary-card",children:[e.jsx("h3",{children:"Configuration"}),e.jsx(v,{label:"Tier",value:`${s.tier} (live)`,title:"Live pressure band — climbs low→mega as context fills the window."}),e.jsx(v,{label:"Preset",value:s.presetTier,title:"The env-resolved base compaction preset (low/medium/high/ultra/mega) that set the token threshold."}),e.jsx(v,{label:"Pressure",value:t,title:"Live pressure = currentTokens / threshold — % of the model context window (threshold fires at the tier's % of window)."}),e.jsx(v,{label:"Threshold",value:a,title:"Compaction threshold = tierPct × model context window — mega-compact trims BELOW pi's native ~80% auto-compact for any model size."}),e.jsx(v,{label:"Fast Gate",value:`${s.fastGatePct}%`,title:"Fast-gate arming floor — the live trim arms once context passes this % of the window."}),e.jsx(v,{label:"Auto",value:s.auto?"enabled":"disabled"}),e.jsx(v,{label:"Anchor",value:String(s.anchorUserMessages)})]})}function T({label:s,value:t,title:a}){return e.jsxs("div",{className:"ov-stat-row",children:[e.jsx("span",{className:"ov-stat-label",title:a,children:s}),e.jsx("span",{className:"ov-stat-value",children:t})]})}function le(s){const t=s.repoTokensFreed||0;let a=0,r="0";if(s.inputRate&&t>0){a=t*s.inputRate;const i=s.contextWindow||0;r=i>0?(t/i).toFixed(1):"0"}const l=`≈ $${a.toFixed(4)} saved`,o=`${r} context-windows extended`;return e.jsxs("div",{className:"card model-cost-card",children:[e.jsx("h3",{children:"💰 Model & Cost Savings"}),e.jsx("div",{className:"ov-cost-usd",children:l}),e.jsx("div",{className:"ov-cost-sub",children:o}),e.jsx(T,{label:"Model",value:s.name,title:"The model pi is currently using — its pricing drives the cost figure."}),e.jsx(T,{label:"Provider",value:s.provider,title:"The provider serving the model."}),e.jsx(T,{label:"Input Rate",value:`$${s.inputRate.toFixed(6)}`,title:"USD per input token, from the model's pricing."}),e.jsx(T,{label:"Output Rate",value:`$${s.outputRate.toFixed(6)}`,title:"USD per output token, from the model's pricing."})]})}function re(s){const{activeAgents:t,currentTurn:a}=s,r=t>0?`▶ ${t} running`:"idle";return e.jsxs("div",{className:"card crew-card",children:[e.jsx("h3",{children:"Crew / Agents"}),e.jsxs("div",{className:"ov-stat-row",children:[e.jsx("span",{className:"ov-stat-label",children:"Active Agents"}),e.jsx("span",{className:"ov-stat-value",children:t})]}),e.jsxs("div",{className:"ov-stat-row",children:[e.jsx("span",{className:"ov-stat-label",children:"Current Turn"}),e.jsx("span",{className:"ov-stat-value",children:a})]}),e.jsxs("div",{className:"ov-stat-row",children:[e.jsx("span",{className:"ov-stat-label",children:"Status"}),e.jsx("span",{className:"ov-stat-value",children:r})]})]})}const ie=[e.jsxs("li",{children:[e.jsx("b",{children:"Original (dropped)"}),' — everything compacted away (including duplicates caught by dedup). The "in."']},"1"),e.jsxs("li",{children:[e.jsx("b",{children:"Kept (summaries)"}),' — compact summaries still held as "memory" (the "out").']},"2"),e.jsxs("li",{children:[e.jsx("b",{children:"Freed"})," = dropped − kept — tokens saved so far (higher = better)."]},"3"),e.jsxs("li",{children:[e.jsx("b",{children:"Compression %"})," — Freed ÷ Dropped — the headline efficiency number. Higher = more space reclaimed."]},"4"),e.jsxs("li",{children:[e.jsx("b",{children:"Storage dedup %"})," — how often new content matched something already saved, so no duplicate copy was written."]},"5"),e.jsxs("li",{children:[e.jsx("b",{children:"Data safety"})," — every compacted region is kept verbatim (compressed). Nothing is permanently deleted; you can restore any of it."]},"6")];function ce(){return e.jsx("div",{className:"card legend-card",children:e.jsxs("details",{children:[e.jsx("summary",{children:"What these numbers mean"}),e.jsx("ol",{className:"legend-list",children:ie}),e.jsx("p",{className:"legend-note",children:"Hover any label above for a quick explanation."})]})})}function de(s){return s.type==="session_sample"}function ue(s){if(!s)return"never";try{return new Date(s).toLocaleString()}catch{return s}}function xe({snapshot:s,loading:t,error:a}){const{data:r,error:l,loading:o,refetch:i}=H(C.useCallback(()=>I(),[]),{pollInterval:2e3}),{events:u}=A(),m=C.useMemo(()=>u.filter(de),[u]),c=C.useMemo(()=>m.length===0?null:m[m.length-1].ts,[m]);if(C.useEffect(()=>{c!=null&&i()},[c,i]),t&&!s)return e.jsx("div",{className:"tab-stub",children:"Loading snapshot…"});if(a&&!s)return e.jsxs("div",{className:"tab-stub",children:["Error: ",a.message]});if(!s)return e.jsx("div",{className:"tab-stub",children:"No snapshot data."});const n=s,{context:h,compression:d,trigger:f,session:P,store:b,tier:D,updatedAt:O}=n,k=n.config,L=n.crew,w=n.integrity,N=n.cacheHits,F=n.compacts,S=n.timeSaved,y=n.repo,j=n.model;return e.jsxs("div",{className:"overview-tab",children:[e.jsxs("div",{className:"overview-header",children:[e.jsx("span",{className:"tier-pill",children:D}),e.jsxs("span",{className:"updated",children:["updated ",ue(O)]})]}),e.jsxs("div",{className:"card-grid overview-card-grid",children:[e.jsx(Y,{sessions:r,loading:o,error:l,launcherSessionId:P.id,launcher:{tokens:h.tokens,percent:h.percent,contextWindow:h.contextWindow}}),e.jsx(V,{sessions:r,loading:o,error:l}),e.jsx(Z,{armed:f.armed,ready:f.ready,currentTokens:f.currentTokens,thresholdTokens:f.thresholdTokens,fastGatePct:f.fastGatePct}),e.jsx(se,{checkpointCount:b.checkpointCount,tokensIn:d.session.tokensIn,tokensOut:d.session.tokensOut,tokensFreed:d.session.tokensFreed,injectedCount:b.injectedCount,dedupHitRate:b.dedupHitRate,storageDedupRate:b.storageDedupRate,dedupCollapsed:b.dedupCollapsed,lastCheckpointId:P.lastCheckpointId,compressionPct:d.session.compressionPct,dedupPct:d.session.dedupPct}),e.jsx(ae,{checkpointCount:y.checkpointCount,tokensIn:d.repo.tokensIn,tokensOut:d.repo.tokensOut,tokensFreed:d.repo.tokensFreed,sessionCount:y.sessionCount,dedupCollapsed:y.dedupCollapsed,storageDedupRate:y.storageDedupRate,compressionPct:d.repo.compressionPct,dedupPct:d.repo.dedupPct}),e.jsx(ne,{regionsRetained:w.regionsRetained,compressedOriginalBytes:w.compressedOriginalBytes,duplicatesCollapsed:w.duplicatesCollapsed,bytesPermanentlyDeleted:w.bytesPermanentlyDeleted}),e.jsx(oe,{tier:D,presetTier:n.presetTier,pressure:n.pressure,thresholdTokens:k.thresholdTokens,tierPct:k.tierPct,contextWindow:h.contextWindow,fastGatePct:k.fastGatePct,auto:k.auto,anchorUserMessages:k.anchorUserMessages}),j&&e.jsx(le,{name:j.name,provider:j.providerName||j.provider,inputRate:j.inputRate,outputRate:j.outputRate,repoTokensFreed:d.repo.tokensFreed,contextWindow:h.contextWindow}),e.jsx(re,{activeAgents:L.activeAgents,currentTurn:L.currentTurn}),e.jsx(_,{cacheHitsSession:N.session,cacheHitsTotal:N.total,tokensSavedSession:N.sessionTokensSaved,tokensSavedTotal:N.totalTokensSaved,compactionsSession:F.session,compactionsTotal:F.total}),e.jsx(G,{compactSessionSec:S.compact.sessionSec,compactTotalSec:S.compact.totalSec,cacheHitSessionSec:S.cacheHit.sessionSec,cacheHitTotalSec:S.cacheHit.totalSec}),e.jsx(ce,{})]})]})}export{xe as default};
2
- //# sourceMappingURL=OverviewTab-CClCUd3-.js.map
1
+ import{j as e,u as H,r as C,f as I}from"./index-BX0bcgLu.js";import{u as A}from"./useSSE-BBYt5UPB.js";import{f as g,a as E,C as _,T as G}from"./TimeSavedCard-UqljSyP-.js";const $=["#58a6ff","#3fb950","#d29922","#f85149","#bc8cff","#ff7b72","#79c0ff","#56d4dd","#e3b341","#a5d6ff","#7ee787","#ffa657"];function B(s){let t=5381;for(let a=0;a<s.length;a++)t=(t<<5)+t+s.charCodeAt(a)>>>0;return t}function U(s){return s?$[B(s)%$.length]:$[0]}function K(s){if(!s)return"(unknown)";const t=s.replace(/[\\/]+$/,"");if(!t)return"(unknown)";const a=t.split(/[\\/]/);return a[a.length-1]||t}function z(s){const t=new Map;for(const o of s){const i=o.repoRoot??"__null__",u=o.tokens??0,m=t.get(i);if(m)m.tokens+=u;else{const c=K(o.repoRoot);t.set(i,{repoRoot:o.repoRoot,basename:c,tokens:u,color:U(c)})}}const a=[...t.values()],r=a.reduce((o,i)=>o+i.tokens,0),l=s.reduce((o,i)=>Math.max(o,i.ctxWindow),0);return{repos:a,totalTokens:r,maxWindow:l,sessionCount:s.length}}function R(s){return s>=1e5?`${(s/1e3).toFixed(0)}k`:s.toLocaleString()}function V({sessions:s,loading:t,error:a}){if(a)return e.jsxs("div",{className:"card repo-stack-card",children:[e.jsx("h3",{children:"Context per Repo (live)"}),e.jsxs("div",{className:"sessions-empty",children:["Error loading sessions: ",a.message]})]});if(t&&!s)return e.jsxs("div",{className:"card repo-stack-card",children:[e.jsx("h3",{children:"Context per Repo (live)"}),e.jsx("div",{className:"sessions-empty",children:"Loading sessions…"})]});const r=s?.sessions??[],{repos:l,totalTokens:o,maxWindow:i,sessionCount:u}=z(r);if(l.length===0)return e.jsxs("div",{className:"card repo-stack-card",children:[e.jsx("h3",{children:"Context per Repo (live)"}),e.jsx("div",{className:"sessions-empty",children:"No active sessions."})]});const m=i>0?Math.round(o/i*100):0;return e.jsxs("div",{className:"card repo-stack-card",children:[e.jsx("h3",{children:"Context per Repo (live)"}),e.jsx("div",{className:"repo-stack-bar",role:"group","aria-label":"Per-repo context usage",children:l.map(c=>{const n=o>0?c.tokens/o*100:0,h=o>0?n:0,d=`${c.basename}: ${R(c.tokens)} tokens (${n.toFixed(1)}% of total)`;return e.jsx("div",{className:"repo-stack-segment",style:{width:`${Math.max(h,.5)}%`,backgroundColor:c.color},title:d,"aria-label":d},c.repoRoot??"__null__")})}),e.jsxs("p",{className:"repo-stack-total",children:[R(o)," tokens across ",l.length," ",l.length===1?"repo":"repos"," /"," ",u," ",u===1?"session":"sessions"," ",e.jsxs("span",{className:"pct",children:["— ",m,"% of combined max window"]})]}),e.jsx("div",{className:"repo-stack-legend",children:l.map(c=>{const n=o>0?c.tokens/o*100:0;return e.jsxs("div",{className:"repo-stack-legend-item",children:[e.jsx("span",{className:"repo-stack-legend-swatch",style:{backgroundColor:c.color}}),e.jsx("span",{className:"repo-stack-legend-name",children:c.basename}),e.jsxs("span",{children:[R(c.tokens)," (",n.toFixed(0),"%)"]})]},c.repoRoot??"__null__")})})]})}function q(s){return s>=90?"gauge-red":s>=70?"gauge-yellow":"gauge-green"}function X(s){return s==null?"?":s>=1e5?`${(s/1e3).toFixed(0)}k`:s.toLocaleString()}function J(s){if(!s)return"(unknown)";const t=s.replace(/[\\/]+$/,"");if(!t)return"(unknown)";const a=t.split(/[\\/]/);return a[a.length-1]||t}function Q(s){const t=Math.max(0,Math.round(s/1e3));if(t<60)return`${t}s ago`;const a=Math.floor(t/60);return a<60?`${a}m ago`:`${Math.floor(a/60)}h ${a%60}m ago`}function W({data:s}){const t=s.percent??0,a=Math.max(t,1),r=q(t),l=X(s.tokens),o=s.contextWindow.toLocaleString(),i=`${l} / ${o} tokens`;return e.jsxs("div",{className:`session-gauge ${s.isSelf?"session-gauge-self":""}`,children:[e.jsxs("div",{className:"session-gauge-head",children:[e.jsx("span",{className:"session-gauge-label",children:s.label}),e.jsxs("span",{className:"session-gauge-pct",children:[t,"%"]})]}),e.jsx("div",{className:`gauge-bar ${r}`,role:"meter","aria-valuenow":t,"aria-valuemin":0,"aria-valuemax":100,"aria-label":`${s.label}: ${t}%`,children:e.jsx("div",{className:"gauge-fill",style:{width:`${a}%`}})}),e.jsx("p",{className:"gauge-label session-gauge-sub",children:i}),s.sublabel&&e.jsx("p",{className:"session-gauge-meta",children:s.sublabel})]})}function Y({sessions:s,loading:t,error:a,launcherSessionId:r,launcher:l}){const o=Date.now(),i=[{key:"self",label:"this session",sublabel:"",tokens:l.tokens,percent:l.percent,contextWindow:l.contextWindow,isSelf:!0}],u=(s?.sessions??[]).filter(n=>n.sessionId!==r);u.sort((n,h)=>h.lastSeen-n.lastSeen);for(const n of u)i.push({key:n.sessionId,label:J(n.repoRoot),sublabel:`pid ${n.pid} · ${Q(o-n.lastSeen)}${n.model?` · ${n.model}`:""}`,tokens:n.tokens,percent:n.percent,contextWindow:n.ctxWindow||0,isSelf:!1});const m=a&&!s,c=t&&!s;return e.jsxs("div",{className:"card session-gauges-card",children:[e.jsx("h3",{children:"Context per Session (live)"}),m?e.jsxs("div",{className:"sessions-empty",children:["Error loading sessions: ",a.message]}):c?e.jsx("div",{className:"sessions-empty",children:"Loading sessions…"}):i.length<=1&&u.length===0?e.jsx("div",{className:"session-gauges-grid",children:i.map(n=>e.jsx(W,{data:n},n.key))}):e.jsx("div",{className:"session-gauges-grid",children:i.map(n=>e.jsx(W,{data:n},n.key))})]})}function M({state:s,label:t}){const a=s==="on"?"bullet-on":s==="na"?"bullet-na":"bullet-off";return e.jsxs("div",{className:`bullet ${a}`,children:[e.jsx("span",{className:"bullet-dot","aria-hidden":"true"}),e.jsx("span",{className:"bullet-label",children:t})]})}function Z({armed:s,ready:t}){const a=s?"on":"off",r=t?"on":s?"off":"na",l=t?"THRESHOLD EXCEEDED — compacting next event":s?"past fast gate — monitoring token count":"idle — below fast gate";return e.jsxs("div",{className:"card trigger-status",children:[e.jsx("h3",{children:"Trigger Status"}),e.jsxs("div",{className:"bullet-row",children:[e.jsx(M,{state:a,label:"Armed"}),e.jsx(M,{state:r,label:"Ready"})]}),e.jsx("p",{className:"trigger-state-text",children:l})]})}function ee(s){return s>=.9?"meter-green":s>=.6?"meter-yellow":"meter-red"}function p({label:s,value:t,title:a}){return e.jsxs("div",{className:"ov-stat-row",children:[e.jsx("span",{className:"ov-stat-label",title:a,children:s}),e.jsx("span",{className:"ov-stat-value",children:t})]})}function se(s){const t=s.compressionPct,a=ee(t),r=Math.max(t*100,.5),l=`${g(t)} tokens saved · dedup: ${g(s.dedupPct)}`;return e.jsxs("div",{className:"card vector-store-card",children:[e.jsx("h3",{children:"Vector Store (session)"}),e.jsx(p,{label:"Checkpoints",value:s.checkpointCount.toLocaleString(),title:"A saved summary of a chunk of your conversation that was compacted to free up space."}),e.jsx(p,{label:"Original (dropped)",value:s.tokensIn.toLocaleString(),title:"Total size of the original conversation text dropped into compaction this session, including redundant regions skipped by dedup. This is the 'in'."}),e.jsx(p,{label:"Kept (summaries)",value:s.tokensOut.toLocaleString(),title:"Compact summaries we are currently holding as 'memory' for this session (the 'out'). Smaller is better."}),e.jsx(p,{label:"Freed",value:s.tokensFreed.toLocaleString(),title:"Conversation space freed = dropped − kept (the 'saved')."}),e.jsx(p,{label:"Injected",value:s.injectedCount,title:"How many times old context was automatically brought back into the conversation because it was relevant to what you were doing."}),e.jsx(p,{label:"Recall Relevance",value:g(s.dedupHitRate),title:"Of the times we recalled old context, how often it was actually on-topic."}),e.jsx(p,{label:"Storage Dedup",value:g(s.storageDedupRate),title:"How often new content matched something we already had, so we skipped storing a duplicate copy. Higher = less wasted space."}),e.jsx(p,{label:"Collapsed",value:s.dedupCollapsed,title:"How many duplicate chunks we collapsed into one instead of storing separately."}),e.jsx(p,{label:"Last ID",value:s.lastCheckpointId??"—",title:"The ID of the most recent saved checkpoint."}),e.jsxs("div",{className:"ov-compression-meter",children:[e.jsx("div",{className:"meter-track",children:e.jsx("div",{className:`meter-fill ${a}`,style:{width:`${r}%`}})}),e.jsx("span",{className:"meter-sub",children:l})]})]})}function te(s){return s>=.9?"meter-green":s>=.6?"meter-yellow":"meter-red"}function x({label:s,value:t}){return e.jsxs("div",{className:"ov-stat-row",children:[e.jsx("span",{className:"ov-stat-label",children:s}),e.jsx("span",{className:"ov-stat-value",children:t})]})}function ae(s){const t=s.compressionPct,a=te(t),r=Math.max(t*100,.5),l=`${g(t)} tokens saved · dedup: ${g(s.dedupPct)}`;return e.jsxs("div",{className:"card repo-sessions-card",children:[e.jsx("h3",{children:"Repo (all sessions)"}),e.jsx(x,{label:"Checkpoints",value:s.checkpointCount.toLocaleString()}),e.jsx(x,{label:"Original",value:s.tokensIn.toLocaleString()}),e.jsx(x,{label:"Kept",value:s.tokensOut.toLocaleString()}),e.jsx(x,{label:"Freed",value:s.tokensFreed.toLocaleString()}),e.jsx(x,{label:"Sessions",value:s.sessionCount.toLocaleString()}),e.jsx(x,{label:"Collapsed",value:s.dedupCollapsed}),e.jsx(x,{label:"Storage Dedup",value:g(s.storageDedupRate)}),e.jsxs("div",{className:"ov-compression-meter",children:[e.jsx("div",{className:"meter-track",children:e.jsx("div",{className:`meter-fill ${a}`,style:{width:`${r}%`}})}),e.jsx("span",{className:"meter-sub",children:l})]})]})}function ne(s){return e.jsxs("div",{className:"card data-safety-card",children:[e.jsx("h3",{children:"🛡 Data Safety"}),e.jsxs("div",{className:"ov-stat-row",children:[e.jsx("span",{className:"ov-stat-label",children:"Regions Retained"}),e.jsx("span",{className:"ov-stat-value",children:s.regionsRetained.toLocaleString()})]}),e.jsxs("div",{className:"ov-stat-row",children:[e.jsx("span",{className:"ov-stat-label",children:"Compressed-Original"}),e.jsx("span",{className:"ov-stat-value",children:E(s.compressedOriginalBytes)})]}),e.jsxs("div",{className:"ov-stat-row",children:[e.jsx("span",{className:"ov-stat-label",children:"Dedup Duplicates"}),e.jsx("span",{className:"ov-stat-value",children:s.duplicatesCollapsed.toLocaleString()})]}),e.jsxs("div",{className:"ov-stat-row",children:[e.jsx("span",{className:"ov-stat-label",children:"Permanently Deleted"}),e.jsx("span",{className:"ov-stat-value ov-value-ok",children:E(s.bytesPermanentlyDeleted)})]}),e.jsx("p",{className:"safe-note",children:'Every compacted region is kept verbatim (compressed). "Drop" = removed from the live window only. We never delete your data.'})]})}function v({label:s,value:t,title:a}){return e.jsxs("div",{className:"ov-stat-row",children:[e.jsx("span",{className:"ov-stat-label",title:a,children:s}),e.jsx("span",{className:"ov-stat-value",children:t})]})}function oe(s){const t=`${Math.round((s.pressure??0)*100)}%`;let a=s.thresholdTokens.toLocaleString();return s.tierPct!=null&&s.contextWindow>0&&(a+=` (${Math.round(s.tierPct*100)}% of ${s.contextWindow.toLocaleString()})`),e.jsxs("div",{className:"card config-summary-card",children:[e.jsx("h3",{children:"Configuration"}),e.jsx(v,{label:"Tier",value:`${s.tier} (live)`,title:"Live pressure band — climbs low→mega as context fills the window."}),e.jsx(v,{label:"Preset",value:s.presetTier,title:"The env-resolved base compaction preset (low/medium/high/ultra/mega) that set the token threshold."}),e.jsx(v,{label:"Pressure",value:t,title:"Live pressure = currentTokens / threshold — % of the model context window (threshold fires at the tier's % of window)."}),e.jsx(v,{label:"Threshold",value:a,title:"Compaction threshold = tierPct × model context window — mega-compact trims BELOW pi's native ~80% auto-compact for any model size."}),e.jsx(v,{label:"Fast Gate",value:`${s.fastGatePct}%`,title:"Fast-gate arming floor — the live trim arms once context passes this % of the window."}),e.jsx(v,{label:"Auto",value:s.auto?"enabled":"disabled"}),e.jsx(v,{label:"Anchor",value:String(s.anchorUserMessages)})]})}function T({label:s,value:t,title:a}){return e.jsxs("div",{className:"ov-stat-row",children:[e.jsx("span",{className:"ov-stat-label",title:a,children:s}),e.jsx("span",{className:"ov-stat-value",children:t})]})}function le(s){const t=s.repoTokensFreed||0;let a=0,r="0";if(s.inputRate&&t>0){a=t*s.inputRate;const i=s.contextWindow||0;r=i>0?(t/i).toFixed(1):"0"}const l=`≈ $${a.toFixed(4)} saved`,o=`${r} context-windows extended`;return e.jsxs("div",{className:"card model-cost-card",children:[e.jsx("h3",{children:"💰 Model & Cost Savings"}),e.jsx("div",{className:"ov-cost-usd",children:l}),e.jsx("div",{className:"ov-cost-sub",children:o}),e.jsx(T,{label:"Model",value:s.name,title:"The model pi is currently using — its pricing drives the cost figure."}),e.jsx(T,{label:"Provider",value:s.provider,title:"The provider serving the model."}),e.jsx(T,{label:"Input Rate",value:`$${s.inputRate.toFixed(6)}`,title:"USD per input token, from the model's pricing."}),e.jsx(T,{label:"Output Rate",value:`$${s.outputRate.toFixed(6)}`,title:"USD per output token, from the model's pricing."})]})}function re(s){const{activeAgents:t,currentTurn:a}=s,r=t>0?`▶ ${t} running`:"idle";return e.jsxs("div",{className:"card crew-card",children:[e.jsx("h3",{children:"Crew / Agents"}),e.jsxs("div",{className:"ov-stat-row",children:[e.jsx("span",{className:"ov-stat-label",children:"Active Agents"}),e.jsx("span",{className:"ov-stat-value",children:t})]}),e.jsxs("div",{className:"ov-stat-row",children:[e.jsx("span",{className:"ov-stat-label",children:"Current Turn"}),e.jsx("span",{className:"ov-stat-value",children:a})]}),e.jsxs("div",{className:"ov-stat-row",children:[e.jsx("span",{className:"ov-stat-label",children:"Status"}),e.jsx("span",{className:"ov-stat-value",children:r})]})]})}const ie=[e.jsxs("li",{children:[e.jsx("b",{children:"Original (dropped)"}),' — everything compacted away (including duplicates caught by dedup). The "in."']},"1"),e.jsxs("li",{children:[e.jsx("b",{children:"Kept (summaries)"}),' — compact summaries still held as "memory" (the "out").']},"2"),e.jsxs("li",{children:[e.jsx("b",{children:"Freed"})," = dropped − kept — tokens saved so far (higher = better)."]},"3"),e.jsxs("li",{children:[e.jsx("b",{children:"Compression %"})," — Freed ÷ Dropped — the headline efficiency number. Higher = more space reclaimed."]},"4"),e.jsxs("li",{children:[e.jsx("b",{children:"Storage dedup %"})," — how often new content matched something already saved, so no duplicate copy was written."]},"5"),e.jsxs("li",{children:[e.jsx("b",{children:"Data safety"})," — every compacted region is kept verbatim (compressed). Nothing is permanently deleted; you can restore any of it."]},"6")];function ce(){return e.jsx("div",{className:"card legend-card",children:e.jsxs("details",{children:[e.jsx("summary",{children:"What these numbers mean"}),e.jsx("ol",{className:"legend-list",children:ie}),e.jsx("p",{className:"legend-note",children:"Hover any label above for a quick explanation."})]})})}function de(s){return s.type==="session_sample"}function ue(s){if(!s)return"never";try{return new Date(s).toLocaleString()}catch{return s}}function xe({snapshot:s,loading:t,error:a}){const{data:r,error:l,loading:o,refetch:i}=H(C.useCallback(()=>I(),[]),{pollInterval:2e3}),{events:u}=A(),m=C.useMemo(()=>u.filter(de),[u]),c=C.useMemo(()=>m.length===0?null:m[m.length-1].ts,[m]);if(C.useEffect(()=>{c!=null&&i()},[c,i]),t&&!s)return e.jsx("div",{className:"tab-stub",children:"Loading snapshot…"});if(a&&!s)return e.jsxs("div",{className:"tab-stub",children:["Error: ",a.message]});if(!s)return e.jsx("div",{className:"tab-stub",children:"No snapshot data."});const n=s,{context:h,compression:d,trigger:f,session:P,store:b,tier:D,updatedAt:O}=n,k=n.config,L=n.crew,w=n.integrity,N=n.cacheHits,F=n.compacts,S=n.timeSaved,y=n.repo,j=n.model;return e.jsxs("div",{className:"overview-tab",children:[e.jsxs("div",{className:"overview-header",children:[e.jsx("span",{className:"tier-pill",children:D}),e.jsxs("span",{className:"updated",children:["updated ",ue(O)]})]}),e.jsxs("div",{className:"card-grid overview-card-grid",children:[e.jsx(Y,{sessions:r,loading:o,error:l,launcherSessionId:P.id,launcher:{tokens:h.tokens,percent:h.percent,contextWindow:h.contextWindow}}),e.jsx(V,{sessions:r,loading:o,error:l}),e.jsx(Z,{armed:f.armed,ready:f.ready,currentTokens:f.currentTokens,thresholdTokens:f.thresholdTokens,fastGatePct:f.fastGatePct}),e.jsx(se,{checkpointCount:b.checkpointCount,tokensIn:d.session.tokensIn,tokensOut:d.session.tokensOut,tokensFreed:d.session.tokensFreed,injectedCount:b.injectedCount,dedupHitRate:b.dedupHitRate,storageDedupRate:b.storageDedupRate,dedupCollapsed:b.dedupCollapsed,lastCheckpointId:P.lastCheckpointId,compressionPct:d.session.compressionPct,dedupPct:d.session.dedupPct}),e.jsx(ae,{checkpointCount:y.checkpointCount,tokensIn:d.repo.tokensIn,tokensOut:d.repo.tokensOut,tokensFreed:d.repo.tokensFreed,sessionCount:y.sessionCount,dedupCollapsed:y.dedupCollapsed,storageDedupRate:y.storageDedupRate,compressionPct:d.repo.compressionPct,dedupPct:d.repo.dedupPct}),e.jsx(ne,{regionsRetained:w.regionsRetained,compressedOriginalBytes:w.compressedOriginalBytes,duplicatesCollapsed:w.duplicatesCollapsed,bytesPermanentlyDeleted:w.bytesPermanentlyDeleted}),e.jsx(oe,{tier:D,presetTier:n.presetTier,pressure:n.pressure,thresholdTokens:k.thresholdTokens,tierPct:k.tierPct,contextWindow:h.contextWindow,fastGatePct:k.fastGatePct,auto:k.auto,anchorUserMessages:k.anchorUserMessages}),j&&e.jsx(le,{name:j.name,provider:j.providerName||j.provider,inputRate:j.inputRate,outputRate:j.outputRate,repoTokensFreed:d.repo.tokensFreed,contextWindow:h.contextWindow}),e.jsx(re,{activeAgents:L.activeAgents,currentTurn:L.currentTurn}),e.jsx(_,{cacheHitsSession:N.session,cacheHitsTotal:N.total,tokensSavedSession:N.sessionTokensSaved,tokensSavedTotal:N.totalTokensSaved,compactionsSession:F.session,compactionsTotal:F.total}),e.jsx(G,{compactSessionSec:S.compact.sessionSec,compactTotalSec:S.compact.totalSec,cacheHitSessionSec:S.cacheHit.sessionSec,cacheHitTotalSec:S.cacheHit.totalSec}),e.jsx(ce,{})]})]})}export{xe as default};
2
+ //# sourceMappingURL=OverviewTab-D4kGvsiQ.js.map