pi-mega-compact 0.11.3 → 0.11.5

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 (307) hide show
  1. package/dist/extensions/dashboard-server/api-contracts/endpoints.js +19 -0
  2. package/dist/extensions/dashboard-server/api-contracts/memory.js +8 -0
  3. package/dist/extensions/dashboard-server/api-contracts/provider-cache.js +1 -0
  4. package/dist/extensions/dashboard-server/api-contracts/setup.js +10 -0
  5. package/dist/extensions/dashboard-server/index-reader.js +6 -2
  6. package/dist/extensions/dashboard-server/routes-cache.js +115 -55
  7. package/dist/extensions/dashboard-server/routes-memory.js +51 -0
  8. package/dist/extensions/dashboard-server/routes-repo.js +2 -2
  9. package/dist/extensions/dashboard-server/routes-setup.js +162 -0
  10. package/dist/extensions/dashboard-server/routes.js +2 -0
  11. package/dist/extensions/dashboard-server/server.js +7 -1
  12. package/dist/extensions/mega-commands.js +189 -1
  13. package/dist/extensions/mega-config.js +3 -0
  14. package/dist/extensions/mega-events/agent-handlers.js +41 -6
  15. package/dist/extensions/mega-events/compact-handlers.js +1 -0
  16. package/dist/extensions/mega-events/context-handler.js +37 -92
  17. package/dist/extensions/mega-events/context-hwm.js +51 -0
  18. package/dist/extensions/mega-events/error-classifier.js +118 -128
  19. package/dist/extensions/mega-events/mirror-append.js +107 -0
  20. package/dist/extensions/mega-events/outage-advisor.js +59 -0
  21. package/dist/extensions/mega-events/perf-handler.js +51 -1
  22. package/dist/extensions/mega-events/recall-tail.js +59 -0
  23. package/dist/extensions/mega-events/session-handlers.js +15 -8
  24. package/dist/extensions/mega-runtime/reset-runtime.js +6 -0
  25. package/dist/extensions/mega-runtime/runtime.js +7 -0
  26. package/dist/src/embedder.js +41 -2
  27. package/dist/src/memoryStats.js +75 -0
  28. package/dist/src/prefix-break.js +43 -0
  29. package/dist/src/pricing.js +1 -1
  30. package/dist/src/store/sqlite/perf-samples.js +144 -16
  31. package/dist/src/store/turns/memory-store.js +8 -0
  32. package/dist/src/store/turns/sqlite-store.js +14 -3
  33. package/dist/src/store/turns/types.js +18 -1
  34. package/dist/src/store/vectorIndex.js +26 -5
  35. package/extensions/dashboard-client/dist/assets/{AchievementTiles-vEo2MtE2.js → AchievementTiles-BoL7qBOJ.js} +2 -2
  36. package/extensions/dashboard-client/dist/assets/{AchievementTiles-vEo2MtE2.js.map → AchievementTiles-BoL7qBOJ.js.map} +1 -1
  37. package/extensions/dashboard-client/dist/assets/AchievementsTab-BFDE3HYc.js +2 -0
  38. package/extensions/dashboard-client/dist/assets/{AchievementsTab-DjxfYrDc.js.map → AchievementsTab-BFDE3HYc.js.map} +1 -1
  39. package/extensions/dashboard-client/dist/assets/CacheTab-eKVc-NV1.js +2 -0
  40. package/extensions/dashboard-client/dist/assets/CacheTab-eKVc-NV1.js.map +1 -0
  41. package/extensions/dashboard-client/dist/assets/{ConfigTab-Cm6ymbmx.js → ConfigTab-DDFE-ckA.js} +2 -2
  42. package/extensions/dashboard-client/dist/assets/{ConfigTab-Cm6ymbmx.js.map → ConfigTab-DDFE-ckA.js.map} +1 -1
  43. package/extensions/dashboard-client/dist/assets/{EventsTab-DXWl_YzI.js → EventsTab-B6UxZ7PM.js} +2 -2
  44. package/extensions/dashboard-client/dist/assets/{EventsTab-DXWl_YzI.js.map → EventsTab-B6UxZ7PM.js.map} +1 -1
  45. package/extensions/dashboard-client/dist/assets/{GameTab-BLDudcO5.js → GameTab-DehUX-KE.js} +2 -2
  46. package/extensions/dashboard-client/dist/assets/{GameTab-BLDudcO5.js.map → GameTab-DehUX-KE.js.map} +1 -1
  47. package/extensions/dashboard-client/dist/assets/{MaintenanceTab-DLkKCztn.js → MaintenanceTab-0GWRt4wn.js} +2 -2
  48. package/extensions/dashboard-client/dist/assets/{MaintenanceTab-DLkKCztn.js.map → MaintenanceTab-0GWRt4wn.js.map} +1 -1
  49. package/extensions/dashboard-client/dist/assets/{MetricsTab-Cq4u-rZF.js → MetricsTab-C3rwMVsN.js} +2 -2
  50. package/extensions/dashboard-client/dist/assets/{MetricsTab-Cq4u-rZF.js.map → MetricsTab-C3rwMVsN.js.map} +1 -1
  51. package/extensions/dashboard-client/dist/assets/OverviewTab-39O9_x5j.js +2 -0
  52. package/extensions/dashboard-client/dist/assets/{OverviewTab-D4kGvsiQ.js.map → OverviewTab-39O9_x5j.js.map} +1 -1
  53. package/extensions/dashboard-client/dist/assets/ReposTab-Cfj6Q5sH.js +2 -0
  54. package/extensions/dashboard-client/dist/assets/ReposTab-Cfj6Q5sH.js.map +1 -0
  55. package/extensions/dashboard-client/dist/assets/{SessionsTab-DZC2-GCn.js → SessionsTab-DXDTCi3r.js} +2 -2
  56. package/extensions/dashboard-client/dist/assets/{SessionsTab-DZC2-GCn.js.map → SessionsTab-DXDTCi3r.js.map} +1 -1
  57. package/extensions/dashboard-client/dist/assets/SetupTab-mMlfunJa.js +2 -0
  58. package/extensions/dashboard-client/dist/assets/SetupTab-mMlfunJa.js.map +1 -0
  59. package/extensions/dashboard-client/dist/assets/TimeSavedCard-Cw1dJ4e9.js +2 -0
  60. package/extensions/dashboard-client/dist/assets/TimeSavedCard-Cw1dJ4e9.js.map +1 -0
  61. package/extensions/dashboard-client/dist/assets/{TopicsTab-01G71hv1.js → TopicsTab-BAw972BV.js} +2 -2
  62. package/extensions/dashboard-client/dist/assets/{TopicsTab-01G71hv1.js.map → TopicsTab-BAw972BV.js.map} +1 -1
  63. package/extensions/dashboard-client/dist/assets/{TurnsTab-DLRg0LZT.js → TurnsTab-Cf5mGIdl.js} +2 -2
  64. package/extensions/dashboard-client/dist/assets/{TurnsTab-DLRg0LZT.js.map → TurnsTab-Cf5mGIdl.js.map} +1 -1
  65. package/extensions/dashboard-client/dist/assets/format-COjDIVGj.js +2 -0
  66. package/extensions/dashboard-client/dist/assets/format-COjDIVGj.js.map +1 -0
  67. package/extensions/dashboard-client/dist/assets/{index-BX0bcgLu.js → index-4gQ7auad.js} +10 -10
  68. package/extensions/dashboard-client/dist/assets/index-4gQ7auad.js.map +1 -0
  69. package/extensions/dashboard-client/dist/assets/pricing-CvE4NllQ.js +2 -0
  70. package/extensions/dashboard-client/dist/assets/pricing-CvE4NllQ.js.map +1 -0
  71. package/extensions/dashboard-client/dist/assets/{useSSE-BBYt5UPB.js → useSSE-DNgMuyP6.js} +2 -2
  72. package/extensions/dashboard-client/dist/assets/{useSSE-BBYt5UPB.js.map → useSSE-DNgMuyP6.js.map} +1 -1
  73. package/extensions/dashboard-client/dist/index.html +1 -1
  74. package/extensions/dashboard-client/src/App.tsx +4 -0
  75. package/extensions/dashboard-client/src/api/client.ts +14 -0
  76. package/extensions/dashboard-client/src/components/ActiveReposTable.tsx +21 -12
  77. package/extensions/dashboard-client/src/components/ProviderCacheCard.tsx +56 -0
  78. package/extensions/dashboard-client/src/components/SavingsByModelTable.tsx +38 -57
  79. package/extensions/dashboard-client/src/tabs/CacheTab.tsx +1 -0
  80. package/extensions/dashboard-client/src/tabs/SetupTab.tsx +355 -0
  81. package/extensions/dashboard-client/tsconfig.json +3 -2
  82. package/extensions/dashboard-client/vite.config.ts +2 -0
  83. package/extensions/dashboard-server/api-contracts/endpoints.ts +36 -3
  84. package/extensions/dashboard-server/api-contracts/index.ts +7 -0
  85. package/extensions/dashboard-server/api-contracts/memory.ts +27 -0
  86. package/extensions/dashboard-server/api-contracts/multi-repo.ts +231 -231
  87. package/extensions/dashboard-server/api-contracts/provider-cache.ts +37 -0
  88. package/extensions/dashboard-server/api-contracts/setup.ts +58 -0
  89. package/extensions/dashboard-server/index-reader.ts +138 -121
  90. package/extensions/dashboard-server/routes-cache.ts +161 -85
  91. package/extensions/dashboard-server/routes-memory.ts +63 -0
  92. package/extensions/dashboard-server/routes-repo.ts +36 -36
  93. package/extensions/dashboard-server/routes-setup.ts +181 -0
  94. package/extensions/dashboard-server/routes.ts +2 -0
  95. package/extensions/dashboard-server/server.ts +6 -0
  96. package/extensions/mega-commands.ts +211 -1
  97. package/extensions/mega-config.ts +18 -0
  98. package/extensions/mega-events/agent-handlers.ts +44 -5
  99. package/extensions/mega-events/compact-handlers.ts +1 -0
  100. package/extensions/mega-events/context-handler.ts +45 -110
  101. package/extensions/mega-events/context-hwm.ts +85 -0
  102. package/extensions/mega-events/error-classifier.ts +156 -124
  103. package/extensions/mega-events/mirror-append.ts +155 -0
  104. package/extensions/mega-events/outage-advisor.ts +79 -0
  105. package/extensions/mega-events/perf-handler.ts +79 -5
  106. package/extensions/mega-events/recall-tail.ts +88 -0
  107. package/extensions/mega-events/session-handlers.ts +14 -7
  108. package/extensions/mega-runtime/helpers.ts +14 -0
  109. package/extensions/mega-runtime/reset-runtime.ts +6 -0
  110. package/extensions/mega-runtime/runtime.ts +7 -0
  111. package/package.json +5 -2
  112. package/src/embedder.ts +42 -2
  113. package/src/memoryStats.ts +107 -0
  114. package/src/prefix-break.ts +83 -0
  115. package/src/pricing.ts +2 -2
  116. package/src/store/sqlite/perf-samples.ts +215 -22
  117. package/src/store/turns/memory-store.ts +10 -1
  118. package/src/store/turns/sqlite-store.ts +27 -15
  119. package/src/store/turns/types.ts +21 -0
  120. package/src/store/vectorIndex.ts +33 -4
  121. package/dist/extensions/conflict-scan.test.js +0 -115
  122. package/dist/extensions/dashboard-server/api-contracts.test.js +0 -884
  123. package/dist/extensions/dashboard-server/html.test.js +0 -52
  124. package/dist/extensions/dashboard-server/perf-server.test.js +0 -80
  125. package/dist/extensions/dashboard-server/routes-cache.test.js +0 -248
  126. package/dist/extensions/dashboard-server/server.test.js +0 -120
  127. package/dist/extensions/dashboard-server-s32.test.js +0 -181
  128. package/dist/extensions/dashboard-server.test.js +0 -339
  129. package/dist/extensions/mega-cache-replay.test.js +0 -400
  130. package/dist/extensions/mega-compact-s38.test.js +0 -820
  131. package/dist/extensions/mega-compact.test.js +0 -1112
  132. package/dist/extensions/mega-events.test.js +0 -47
  133. package/dist/extensions/mega-game-cmds.test.js +0 -163
  134. package/dist/extensions/mega-runtime/snapshot.test.js +0 -127
  135. package/dist/extensions/mega-runtime/state.test.js +0 -266
  136. package/dist/extensions/mega-runtime/widget.test.js +0 -343
  137. package/dist/extensions/mega-shutdown-widget.test.js +0 -121
  138. package/dist/extensions/mega-teamrun.test.js +0 -156
  139. package/dist/extensions/mega-trim.test.js +0 -58
  140. package/dist/extensions/mega-turn-store.test.js +0 -68
  141. package/dist/src/boundary.test.js +0 -170
  142. package/dist/src/compact.test.js +0 -78
  143. package/dist/src/config/themes.test.js +0 -94
  144. package/dist/src/dedup/dedup.test.js +0 -41
  145. package/dist/src/dedup/l1.test.js +0 -50
  146. package/dist/src/dedup/raptor/buildHistory.test.js +0 -292
  147. package/dist/src/dedup/raptor/multilevel-serve.test.js +0 -229
  148. package/dist/src/dedup/raptor/multilevel.test.js +0 -238
  149. package/dist/src/dedup/raptor/promote.test.js +0 -69
  150. package/dist/src/dedup/raptor/raptor.test.js +0 -248
  151. package/dist/src/dedup/raptor/retrieval.test.js +0 -95
  152. package/dist/src/dedup/raptor/serve-gate.test.js +0 -298
  153. package/dist/src/dedup/sprint12.test.js +0 -219
  154. package/dist/src/dedup-engine.test.js +0 -472
  155. package/dist/src/driftDetection.test.js +0 -87
  156. package/dist/src/e2e.test.js +0 -698
  157. package/dist/src/engine.test.js +0 -111
  158. package/dist/src/extractive.test.js +0 -147
  159. package/dist/src/fork.test.js +0 -81
  160. package/dist/src/game/scoring.test.js +0 -98
  161. package/dist/src/httpEmbedder.test.js +0 -277
  162. package/dist/src/importance.test.js +0 -372
  163. package/dist/src/intent.test.js +0 -78
  164. package/dist/src/log.test.js +0 -42
  165. package/dist/src/mechanical-fix.test.js +0 -65
  166. package/dist/src/memory.test.js +0 -70
  167. package/dist/src/memoryConsolidate.test.js +0 -38
  168. package/dist/src/memoryOps.test.js +0 -129
  169. package/dist/src/memoryRecall.test.js +0 -171
  170. package/dist/src/memoryRoundtrip.test.js +0 -137
  171. package/dist/src/metrics/turns.test.js +0 -152
  172. package/dist/src/mirror/mirror.test.js +0 -185
  173. package/dist/src/raptor-inject-summaries.test.js +0 -162
  174. package/dist/src/ratio.bench.test.js +0 -897
  175. package/dist/src/recall.integration.test.js +0 -77
  176. package/dist/src/recall.test.js +0 -379
  177. package/dist/src/sprint14.test.js +0 -219
  178. package/dist/src/sprint4x-rag-verification.test.js +0 -93
  179. package/dist/src/store/compression.test.js +0 -139
  180. package/dist/src/store/memoryIndex.test.js +0 -51
  181. package/dist/src/store/migrate.test.js +0 -139
  182. package/dist/src/store/pgOpenGuard.test.js +0 -74
  183. package/dist/src/store/phase01.test.js +0 -81
  184. package/dist/src/store/phase04.test.js +0 -54
  185. package/dist/src/store/sprint10.test.js +0 -186
  186. package/dist/src/store/sqlite/game-achievements.test.js +0 -67
  187. package/dist/src/store/sqlite/game-scores.test.js +0 -106
  188. package/dist/src/store/sqlite/game-state.test.js +0 -76
  189. package/dist/src/store/sqlite/global-index.test.js +0 -136
  190. package/dist/src/store/sqlite/mechanical-fix.test.js +0 -146
  191. package/dist/src/store/sqlite/perf-samples.test.js +0 -146
  192. package/dist/src/store/sqlite/raptor.test.js +0 -106
  193. package/dist/src/store/sqlite/turns.test.js +0 -183
  194. package/dist/src/store/sqlite.cachehit.test.js +0 -55
  195. package/dist/src/store/sqlite.dbmaint.test.js +0 -158
  196. package/dist/src/store/sqlite.dbmirror.test.js +0 -184
  197. package/dist/src/store/turns/contract-compliance.test.js +0 -428
  198. package/dist/src/store/turns/memory-store.test.js +0 -9
  199. package/dist/src/store/turns/migrations.test.js +0 -138
  200. package/dist/src/store/turns/sqlite-store.test.js +0 -144
  201. package/dist/src/store/vectorIndex.test.js +0 -123
  202. package/dist/src/store.test.js +0 -161
  203. package/dist/src/supersede.test.js +0 -36
  204. package/dist/src/topics/cluster.test.js +0 -150
  205. package/dist/src/topics/store.test.js +0 -117
  206. package/dist/src/types.test.js +0 -9
  207. package/dist/src/vector-search-cache.test.js +0 -157
  208. package/dist/src/vectorStore.test.js +0 -479
  209. package/dist/src/wiki.test.js +0 -171
  210. package/extensions/conflict-scan.test.ts +0 -129
  211. package/extensions/dashboard-client/dist/assets/AchievementsTab-DjxfYrDc.js +0 -2
  212. package/extensions/dashboard-client/dist/assets/CacheTab-Dnpv94wq.js +0 -2
  213. package/extensions/dashboard-client/dist/assets/CacheTab-Dnpv94wq.js.map +0 -1
  214. package/extensions/dashboard-client/dist/assets/OverviewTab-D4kGvsiQ.js +0 -2
  215. package/extensions/dashboard-client/dist/assets/ReposTab-C0Wnrf2u.js +0 -2
  216. package/extensions/dashboard-client/dist/assets/ReposTab-C0Wnrf2u.js.map +0 -1
  217. package/extensions/dashboard-client/dist/assets/TimeSavedCard-UqljSyP-.js +0 -2
  218. package/extensions/dashboard-client/dist/assets/TimeSavedCard-UqljSyP-.js.map +0 -1
  219. package/extensions/dashboard-client/dist/assets/index-BX0bcgLu.js.map +0 -1
  220. package/extensions/dashboard-server/api-contracts.test.ts +0 -1065
  221. package/extensions/dashboard-server/html.test.ts +0 -62
  222. package/extensions/dashboard-server/perf-server.test.ts +0 -101
  223. package/extensions/dashboard-server/routes-cache.test.ts +0 -302
  224. package/extensions/dashboard-server/server.test.ts +0 -131
  225. package/extensions/dashboard-server-s32.test.ts +0 -195
  226. package/extensions/dashboard-server.test.ts +0 -382
  227. package/extensions/mega-cache-replay.test.ts +0 -526
  228. package/extensions/mega-compact-s38.test.ts +0 -872
  229. package/extensions/mega-compact.test.ts +0 -1509
  230. package/extensions/mega-events.test.ts +0 -55
  231. package/extensions/mega-game-cmds.test.ts +0 -197
  232. package/extensions/mega-runtime/snapshot.test.ts +0 -139
  233. package/extensions/mega-runtime/state.test.ts +0 -262
  234. package/extensions/mega-runtime/widget.test.ts +0 -468
  235. package/extensions/mega-shutdown-widget.test.ts +0 -141
  236. package/extensions/mega-teamrun.test.ts +0 -176
  237. package/extensions/mega-trim.test.ts +0 -64
  238. package/extensions/mega-turn-store.test.ts +0 -108
  239. package/src/boundary.test.ts +0 -187
  240. package/src/compact.test.ts +0 -99
  241. package/src/config/themes.test.ts +0 -116
  242. package/src/dedup/dedup.test.ts +0 -46
  243. package/src/dedup/l1.test.ts +0 -57
  244. package/src/dedup/raptor/buildHistory.test.ts +0 -353
  245. package/src/dedup/raptor/multilevel-serve.test.ts +0 -273
  246. package/src/dedup/raptor/multilevel.test.ts +0 -325
  247. package/src/dedup/raptor/promote.test.ts +0 -82
  248. package/src/dedup/raptor/raptor.test.ts +0 -297
  249. package/src/dedup/raptor/retrieval.test.ts +0 -118
  250. package/src/dedup/raptor/serve-gate.test.ts +0 -348
  251. package/src/dedup/sprint12.test.ts +0 -242
  252. package/src/dedup-engine.test.ts +0 -670
  253. package/src/driftDetection.test.ts +0 -100
  254. package/src/e2e.test.ts +0 -843
  255. package/src/engine.test.ts +0 -123
  256. package/src/extractive.test.ts +0 -174
  257. package/src/fork.test.ts +0 -94
  258. package/src/game/scoring.test.ts +0 -103
  259. package/src/httpEmbedder.test.ts +0 -286
  260. package/src/importance.test.ts +0 -538
  261. package/src/intent.test.ts +0 -87
  262. package/src/log.test.ts +0 -47
  263. package/src/mechanical-fix.test.ts +0 -70
  264. package/src/memory.test.ts +0 -92
  265. package/src/memoryConsolidate.test.ts +0 -47
  266. package/src/memoryOps.test.ts +0 -148
  267. package/src/memoryRecall.test.ts +0 -194
  268. package/src/memoryRoundtrip.test.ts +0 -155
  269. package/src/metrics/turns.test.ts +0 -165
  270. package/src/mirror/mirror.test.ts +0 -240
  271. package/src/raptor-inject-summaries.test.ts +0 -228
  272. package/src/ratio.bench.test.ts +0 -1316
  273. package/src/recall.integration.test.ts +0 -96
  274. package/src/recall.test.ts +0 -467
  275. package/src/sprint14.test.ts +0 -245
  276. package/src/sprint4x-rag-verification.test.ts +0 -119
  277. package/src/store/compression.test.ts +0 -168
  278. package/src/store/memoryIndex.test.ts +0 -61
  279. package/src/store/migrate.test.ts +0 -158
  280. package/src/store/pgOpenGuard.test.ts +0 -89
  281. package/src/store/phase01.test.ts +0 -88
  282. package/src/store/phase04.test.ts +0 -58
  283. package/src/store/sprint10.test.ts +0 -214
  284. package/src/store/sqlite/game-achievements.test.ts +0 -80
  285. package/src/store/sqlite/game-scores.test.ts +0 -132
  286. package/src/store/sqlite/game-state.test.ts +0 -89
  287. package/src/store/sqlite/global-index.test.ts +0 -222
  288. package/src/store/sqlite/mechanical-fix.test.ts +0 -162
  289. package/src/store/sqlite/perf-samples.test.ts +0 -169
  290. package/src/store/sqlite/raptor.test.ts +0 -139
  291. package/src/store/sqlite/turns.test.ts +0 -218
  292. package/src/store/sqlite.cachehit.test.ts +0 -68
  293. package/src/store/sqlite.dbmaint.test.ts +0 -193
  294. package/src/store/sqlite.dbmirror.test.ts +0 -229
  295. package/src/store/turns/contract-compliance.test.ts +0 -562
  296. package/src/store/turns/memory-store.test.ts +0 -15
  297. package/src/store/turns/migrations.test.ts +0 -161
  298. package/src/store/turns/sqlite-store.test.ts +0 -161
  299. package/src/store/vectorIndex.test.ts +0 -140
  300. package/src/store.test.ts +0 -191
  301. package/src/supersede.test.ts +0 -42
  302. package/src/topics/cluster.test.ts +0 -176
  303. package/src/topics/store.test.ts +0 -132
  304. package/src/types.test.ts +0 -10
  305. package/src/vector-search-cache.test.ts +0 -190
  306. package/src/vectorStore.test.ts +0 -533
  307. package/src/wiki.test.ts +0 -217
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CacheTab-eKVc-NV1.js","sources":["../../src/components/ProviderCacheCard.tsx","../../src/tabs/CacheTab.tsx"],"sourcesContent":["/**\n * dashboard-client/src/components/ProviderCacheCard.tsx — Provider prompt cache stats card.\n *\n * Displays lifetime provider prompt cache hit-rate aggregates + estimated\n * dollar savings from the /api/provider-cache endpoint.\n * Reuses the `ov-stat-row` / `StatRow` pattern from CacheHitsCard.tsx.\n *\n * B.2 spec: Cache Hit Rate with color classes, humanized token counts,\n * priced $ rows with green/red net, model name, locale date + relative time.\n */\n\nimport type React from \"react\";\nimport {\n\tfmtTokens,\n\tfmtDollars,\n\tfmtDate,\n\tfmtRelativeTime,\n} from \"../utils/format\";\nimport { lookupModelInputRate } from \"@pricing\";\n\n// ---------------------------------------------------------------------------\n// Per-model breakdown type (mirrors the API contract).\n// ---------------------------------------------------------------------------\n\nexport interface ByModelEntry {\n\tmodel: string;\n\thitPct: number;\n\ttotalCacheRead: number;\n\ttotalCacheWrite: number;\n\tsampleCount: number;\n}\n\n// ---------------------------------------------------------------------------\n// Flattened props — destructured from ProviderCacheResponse in CacheTab.\n// ---------------------------------------------------------------------------\n\nexport interface ProviderCacheCardProps {\n\t/** Cache hit rate percentage (0–100). */\n\thitPct: number;\n\t/** Number of turns with cache_hit_pct samples. */\n\tturnCount: number;\n\t/** Total cache-read tokens across all sampled turns. */\n\ttotalCacheRead: number;\n\t/** Total cache-write tokens across all sampled turns. */\n\ttotalCacheWrite: number;\n\t/** Total input tokens across all sampled turns. */\n\ttotalInput: number;\n\t/** $ saved from cache reads (null when unpriced). */\n\tcacheReadSaved: number | null;\n\t/** $ spent on cache writes (null when unpriced). */\n\tcacheWriteCost: number | null;\n\t/** netSaved = cacheReadSaved - cacheWriteCost (null when unpriced). */\n\tnetSaved: number | null;\n\t/** Model display name (modelName || modelId, or null). */\n\tmodelLabel: string | null;\n\t/** ISO timestamp of first recorded turn. */\n\tfirstTurnAt: string | null;\n\t/** ISO timestamp of latest recorded turn. */\n\tlatestTurnAt: string | null;\n\t/** Per-model breakdown (F4). */\n\tbyModel: ByModelEntry[];\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** Hit-rate color: green ≥80, yellow ≥50, red <50. */\nfunction hitPctClass(pct: number): string {\n\tif (pct >= 80) return \"ov-stat-value-green\";\n\tif (pct >= 50) return \"ov-stat-value-yellow\";\n\treturn \"ov-stat-value-red\";\n}\n\n/** Net-saved color: green positive, red negative. */\nfunction netClass(v: number | null): string {\n\tif (v == null) return \"\";\n\treturn v >= 0 ? \"ov-stat-value-green\" : \"ov-stat-value-red\";\n}\n\nfunction StatRow({\n\tlabel,\n\tvalue,\n\tclassName,\n\ttitle,\n}: {\n\tlabel: string;\n\tvalue: string;\n\tclassName?: string;\n\ttitle?: string;\n}): React.ReactElement {\n\treturn (\n\t\t<div className=\"ov-stat-row\">\n\t\t\t<span className=\"ov-stat-label\" title={title}>\n\t\t\t\t{label}\n\t\t\t</span>\n\t\t\t<span className={`ov-stat-value${className ? ` ${className}` : \"\"}`}>\n\t\t\t\t{value}\n\t\t\t</span>\n\t\t</div>\n\t);\n}\n\n// ---------------------------------------------------------------------------\n// Card\n// ---------------------------------------------------------------------------\n\nexport function ProviderCacheCard(\n\tprops: ProviderCacheCardProps,\n): React.ReactElement {\n\treturn (\n\t\t<div className=\"card provider-cache-card\">\n\t\t\t{/* --- Hit Rate + Turns --- */}\n\t\t\t<StatRow\n\t\t\t\tlabel=\"Cache Hit Rate\"\n\t\t\t\tvalue={`${props.hitPct.toFixed(1)}%`}\n\t\t\t\tclassName={hitPctClass(props.hitPct)}\n\t\t\t/>\n\t\t\t<StatRow label=\"Turns Recorded\" value={String(props.turnCount)} />\n\n\t\t\t{/* --- Token counts --- */}\n\t\t\t<StatRow\n\t\t\t\tlabel=\"Cache Read Tokens\"\n\t\t\t\tvalue={fmtTokens(props.totalCacheRead)}\n\t\t\t/>\n\t\t\t<StatRow\n\t\t\t\tlabel=\"Cache Write Tokens\"\n\t\t\t\tvalue={fmtTokens(props.totalCacheWrite)}\n\t\t\t/>\n\t\t\t<StatRow label=\"Input Tokens\" value={fmtTokens(props.totalInput)} />\n\n\t\t\t{/* --- Dollar rows (null when unpriced) --- */}\n\t\t\t<StatRow\n\t\t\t\tlabel=\"$ Saved (reads)\"\n\t\t\t\tvalue={fmtDollars(props.cacheReadSaved)}\n\t\t\t/>\n\t\t\t<StatRow\n\t\t\t\tlabel=\"Write Investment\"\n\t\t\t\tvalue={fmtDollars(props.cacheWriteCost)}\n\t\t\t/>\n\t\t\t<StatRow\n\t\t\t\tlabel=\"Net Saved\"\n\t\t\t\tvalue={fmtDollars(props.netSaved)}\n\t\t\t\tclassName={netClass(props.netSaved)}\n\t\t\t/>\n\n\t\t\t{/* --- Model + timestamps --- */}\n\t\t\t<StatRow label=\"Model\" value={props.modelLabel ?? \"\\u2014\"} />\n\t\t\t<StatRow label=\"Tracked Since\" value={fmtDate(props.firstTurnAt)} />\n\t\t\t<StatRow\n\t\t\t\tlabel=\"Last Updated\"\n\t\t\t\tvalue={fmtRelativeTime(props.latestTurnAt)}\n\t\t\t/>\n\n\t\t\t{/* --- Per-model breakdown (F4) --- */}\n\t\t\t{props.byModel.length > 0 && (\n\t\t\t\t<>\n\t\t\t\t\t<div className=\"ov-stat-section-label\">Per Model</div>\n\t\t\t\t\t{props.byModel.map((m) => {\n\t\t\t\t\t\tconst rate = lookupModelInputRate(m.model);\n\t\t\t\t\t\tconst cr =\n\t\t\t\t\t\t\trate != null ? (m.totalCacheRead / 1_000_000) * rate : null;\n\t\t\t\t\t\tconst cw =\n\t\t\t\t\t\t\trate != null ? (m.totalCacheWrite / 1_000_000) * rate : null;\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t<div key={m.model} className=\"ov-stat-group\">\n\t\t\t\t\t\t\t\t<StatRow label=\"Model\" value={m.model} />\n\t\t\t\t\t\t\t\t<StatRow\n\t\t\t\t\t\t\t\t\tlabel=\"Hit Rate\"\n\t\t\t\t\t\t\t\t\tvalue={`${m.hitPct.toFixed(1)}%`}\n\t\t\t\t\t\t\t\t\tclassName={hitPctClass(m.hitPct)}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t<StatRow\n\t\t\t\t\t\t\t\t\tlabel=\"Cache Read\"\n\t\t\t\t\t\t\t\t\tvalue={fmtTokens(m.totalCacheRead)}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t<StatRow\n\t\t\t\t\t\t\t\t\tlabel=\"Cache Write\"\n\t\t\t\t\t\t\t\t\tvalue={fmtTokens(m.totalCacheWrite)}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t<StatRow label=\"Samples\" value={String(m.sampleCount)} />\n\t\t\t\t\t\t\t\t<StatRow\n\t\t\t\t\t\t\t\t\tlabel=\"$ Saved (reads)\"\n\t\t\t\t\t\t\t\t\tvalue={cr != null ? fmtDollars(cr) : \"\\u2014\"}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t<StatRow\n\t\t\t\t\t\t\t\t\tlabel=\"Write Investment\"\n\t\t\t\t\t\t\t\t\tvalue={cw != null ? fmtDollars(cw) : \"\\u2014\"}\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)}\n\t\t</div>\n\t);\n}\n","/**\n * dashboard-client/src/tabs/CacheTab.tsx — Cache tab (B.3).\n *\n * Two sections, each with its own header:\n * 1. Provider Prompt Cache → ProviderCacheCard (/api/provider-cache)\n * 2. Mega-Compact Dedup Cache → CacheHitsCard + TimeSavedCard (/api/snapshot)\n *\n * Both fetch at 5s polling (same interval). Loading / error shown per-section\n * only when its own fetch fails; the other section keeps rendering.\n */\n\nimport type React from \"react\";\nimport { useCallback } from \"react\";\nimport type { SnapshotResponse, ProviderCacheResponse } from \"@contracts\";\nimport { useApi } from \"../hooks/useApi\";\nimport { fetchSnapshot, fetchProviderCache } from \"../api/client\";\nimport { CacheHitsCard } from \"../components/CacheHitsCard\";\nimport { TimeSavedCard } from \"../components/TimeSavedCard\";\nimport { ProviderCacheCard } from \"../components/ProviderCacheCard\";\n\nexport default function CacheTab(): React.ReactElement {\n\t/* --- Provider prompt cache (5s poll) --- */\n\tconst {\n\t\tdata: providerCache,\n\t\tloading: pcLoading,\n\t\terror: pcError,\n\t} = useApi<ProviderCacheResponse>(\n\t\tuseCallback(() => fetchProviderCache(), []),\n\t\t{ pollInterval: 5000 },\n\t);\n\n\t/* --- Dedup cache snapshot (5s poll) --- */\n\tconst {\n\t\tdata: snapshot,\n\t\tloading: snapLoading,\n\t\terror: snapError,\n\t} = useApi<SnapshotResponse>(\n\t\tuseCallback(() => fetchSnapshot(), []),\n\t\t{ pollInterval: 5000 },\n\t);\n\n\treturn (\n\t\t<div className=\"cache-tab\">\n\t\t\t{/* ==============================================================\n\t\t\t Section 1 — Provider Prompt Cache\n\t\t\t ============================================================== */}\n\t\t\t<h2>Provider Prompt Cache</h2>\n\t\t\t{pcLoading ? (\n\t\t\t\t<p className=\"tab-stub\">Loading…</p>\n\t\t\t) : pcError ? (\n\t\t\t\t<p className=\"tab-stub\">\n\t\t\t\t\tProvider cache unavailable: {pcError.message}\n\t\t\t\t</p>\n\t\t\t) : providerCache ? (\n\t\t\t\t<ProviderCacheCard\n\t\t\t\t\thitPct={providerCache.cache.avgHitPct}\n\t\t\t\t\tturnCount={providerCache.cache.turnCount}\n\t\t\t\t\ttotalCacheRead={providerCache.cache.totalCacheRead}\n\t\t\t\t\ttotalCacheWrite={providerCache.cache.totalCacheWrite}\n\t\t\t\t\ttotalInput={providerCache.cache.totalInput}\n\t\t\t\t\tcacheReadSaved={providerCache.savings?.cacheReadSaved ?? null}\n\t\t\t\t\tcacheWriteCost={providerCache.savings?.cacheWriteCost ?? null}\n\t\t\t\t\tnetSaved={providerCache.savings?.netSaved ?? null}\n\t\t\t\t\tmodelLabel={providerCache.savings?.model ?? null}\n\t\t\t\t\tfirstTurnAt={providerCache.cache.firstTurnAt}\n\t\t\t\t\tlatestTurnAt={providerCache.cache.latestTurnAt}\n\t\t\t\t\t\t\t\t\tbyModel={providerCache.cache.byModel ?? []}\n\t\t\t\t/>\n\t\t\t) : null}\n\n\t\t\t{/* ==============================================================\n\t\t\t Section 2 — Mega-Compact Dedup Cache\n\t\t\t ============================================================== */}\n\t\t\t<h2>Mega-Compact Dedup Cache</h2>\n\t\t\t{snapLoading ? (\n\t\t\t\t<p className=\"tab-stub\">Loading…</p>\n\t\t\t) : snapError ? (\n\t\t\t\t<p className=\"tab-stub\">Snapshot unavailable: {snapError.message}</p>\n\t\t\t) : (\n\t\t\t\tsnapshot &&\n\t\t\t\t(() => {\n\t\t\t\t\tconst { cacheHits, compacts, timeSaved } = snapshot;\n\t\t\t\t\treturn (\n\t\t\t\t\t\t<div className=\"card-grid overview-card-grid\">\n\t\t\t\t\t\t\t<CacheHitsCard\n\t\t\t\t\t\t\t\tcacheHitsSession={cacheHits.session}\n\t\t\t\t\t\t\t\tcacheHitsTotal={cacheHits.total}\n\t\t\t\t\t\t\t\ttokensSavedSession={cacheHits.sessionTokensSaved}\n\t\t\t\t\t\t\t\ttokensSavedTotal={cacheHits.totalTokensSaved}\n\t\t\t\t\t\t\t\tcompactionsSession={compacts.session}\n\t\t\t\t\t\t\t\tcompactionsTotal={compacts.total}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<TimeSavedCard\n\t\t\t\t\t\t\t\tcompactSessionSec={timeSaved.compact.sessionSec}\n\t\t\t\t\t\t\t\tcompactTotalSec={timeSaved.compact.totalSec}\n\t\t\t\t\t\t\t\tcacheHitSessionSec={timeSaved.cacheHit.sessionSec}\n\t\t\t\t\t\t\t\tcacheHitTotalSec={timeSaved.cacheHit.totalSec}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t);\n\t\t\t\t})()\n\t\t\t)}\n\t\t</div>\n\t);\n}\n"],"names":["hitPctClass","pct","netClass","v","StatRow","label","value","className","title","jsxs","jsx","ProviderCacheCard","props","fmtTokens","fmtDollars","fmtDate","fmtRelativeTime","Fragment","m","rate","lookupModelInputRate","cr","cw","CacheTab","providerCache","pcLoading","pcError","useApi","useCallback","fetchProviderCache","snapshot","snapLoading","snapError","fetchSnapshot","cacheHits","compacts","timeSaved","CacheHitsCard","TimeSavedCard"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import{r as i,p as N,j as e,c as w,d as y}from"./index-BX0bcgLu.js";const b=[{id:"transparent",label:"Transparent (accents only)",css:{bg:null,fg:"#c9d1d9",accent:"#3fb950",mega:"#f0883e"},ansi:{bg:null,fg:"39",accent:"32",mega:"33"}},{id:"retro",label:"Retro 8-bit green",css:{bg:"#003300",fg:"#33ff33",accent:"#00ff41",mega:"#39ff14"},ansi:{bg:"40",fg:"92",accent:"92",mega:"1;92"}},{id:"orange-bold",label:"Orange bold",css:{bg:"#1a0f00",fg:"#ff8c00",accent:"#ff6b00",mega:"#ff4500"},ansi:{bg:"48;5;52",fg:"38;5;208",accent:"38;5;208",mega:"1;38;5;202"}},{id:"cyan-neon",label:"Cyan neon",css:{bg:"#001a1a",fg:"#00ffff",accent:"#22d3ee",mega:"#7df9ff"},ansi:{bg:"48;5;23",fg:"38;5;51",accent:"38;5;51",mega:"1;38;5;51"}},{id:"amber-mono",label:"Amber phosphor",css:{bg:"#1a1200",fg:"#ffb000",accent:"#ff8c00",mega:"#ff5500"},ansi:{bg:"48;5;58",fg:"38;5;214",accent:"38;5;214",mega:"1;38;5;208"}},{id:"grayscale",label:"Grayscale minimal",css:{bg:"#161616",fg:"#b0b0b0",accent:"#8b949e",mega:"#e6edf3"},ansi:{bg:"48;5;235",fg:"38;5;249",accent:"38;5;249",mega:"1;38;5;255"}}],g=b.map(t=>t.id),c={gameMode:"Turn game mode on/off (themes the widget + dashboard)",theme:"Visual theme (applies instantly)",tuiMode:"TUI widget display density",tier:"Live pressure band — climbs low→mega as context fills the window.",preset:"The env-resolved base compaction preset (low/medium/high/ultra/mega) that set the token threshold.",pressure:"Live pressure = currentTokens / threshold — % of the model context window (threshold fires at the tier's % of window).",threshold:"Compaction threshold = tierPct × model context window — mega-compact trims BELOW pi's native ~80% auto-compact for any model size.",fastGate:"Fast-gate arming floor — the live trim arms once context passes this % of the window."};function T(t,d,l){let o=t.toLocaleString();return d!=null&&l>0&&(o+=` (${Math.round(d*100)}% of ${l.toLocaleString()})`),o}function S(t){return`${Math.round((t||0)*100)}%`}function E(){const[t,d]=i.useState(null),[l,o]=i.useState(null),[m,u]=i.useState(!1);i.useEffect(()=>{let s=!0;const a=async()=>{try{const r=await w();s&&d(r)}catch{}};a();const f=setInterval(()=>void a(),5e3);return()=>{s=!1,clearInterval(f)}},[]),i.useEffect(()=>{let s=!0;const a=async()=>{try{const r=await y();s&&(o(r),r.theme&&(document.documentElement.dataset.theme=r.theme))}catch{}};a();const f=setInterval(()=>void a(),5e3);return()=>{s=!1,clearInterval(f)}},[]);const h=i.useCallback(async s=>{u(!0);try{const a=await N(s);o(a),a.theme&&(document.documentElement.dataset.theme=a.theme)}catch{}u(!1)},[]),x=l?.game_mode_on??!1,p=l?.theme??"transparent",v=l?.tui_display_mode??"full",n=t?.config,j=t?.context.contextWindow??0;return e.jsxs("div",{className:"config-tab",children:[e.jsxs("section",{className:"config-settings",children:[e.jsx("h3",{children:"Settings"}),e.jsx("div",{className:"settings-row",children:e.jsxs("label",{title:c.gameMode,children:[e.jsx("input",{type:"checkbox",checked:x,onChange:s=>h({game_mode_on:s.currentTarget.checked}),disabled:m}),"Game Mode"]})}),e.jsx("div",{className:"settings-row",children:e.jsxs("label",{title:c.theme,children:["Theme",e.jsx("select",{value:p,onChange:s=>h({theme:s.currentTarget.value}),disabled:m,children:b.map(s=>e.jsx("option",{value:s.id,children:s.label},s.id))}),e.jsx("button",{type:"button",disabled:m,className:"theme-cycle-btn",title:"Cycle to the next theme (P3)",onClick:()=>{const s=g.indexOf(p),a=g[(s<0?0:s+1)%g.length];a&&h({theme:a})},children:"Next theme →"})]})}),e.jsx("div",{className:"settings-row",children:e.jsxs("label",{title:c.tuiMode,children:["TUI Mode",e.jsxs("select",{value:v,onChange:s=>h({tui_display_mode:s.currentTarget.value}),disabled:m,children:[e.jsx("option",{value:"full",children:"Full"}),e.jsx("option",{value:"minimal",children:"Minimal"})]})," ",e.jsx("span",{className:"tui-mode-hint",children:"(affects the in-app TUI widget; not shown here)"})]})})]}),e.jsxs("section",{className:"config-display",children:[e.jsx("h3",{children:"Configuration"}),e.jsxs("div",{className:"config-grid",children:[e.jsxs("div",{className:"config-item",children:[e.jsx("span",{className:"config-label",title:c.tier,children:"Tier"}),e.jsx("span",{className:"config-value",children:t?`${t.tier} (live)`:"—"})]}),e.jsxs("div",{className:"config-item",children:[e.jsx("span",{className:"config-label",title:c.preset,children:"Preset"}),e.jsx("span",{className:"config-value",children:t?.presetTier??"—"})]}),e.jsxs("div",{className:"config-item",children:[e.jsx("span",{className:"config-label",title:c.pressure,children:"Pressure"}),e.jsx("span",{className:"config-value",children:t?S(t.pressure):"—"})]}),e.jsxs("div",{className:"config-item",children:[e.jsx("span",{className:"config-label",title:c.threshold,children:"Threshold"}),e.jsx("span",{className:"config-value",children:n?T(n.thresholdTokens,n.tierPct,j):"—"})]}),e.jsxs("div",{className:"config-item",children:[e.jsx("span",{className:"config-label",title:c.fastGate,children:"Fast Gate"}),e.jsx("span",{className:"config-value",children:n?`${n.fastGatePct}%`:"—"})]}),e.jsxs("div",{className:"config-item",children:[e.jsx("span",{className:"config-label",children:"Auto"}),e.jsx("span",{className:"config-value",children:n?n.auto?"enabled":"disabled":"—"})]}),e.jsxs("div",{className:"config-item",children:[e.jsx("span",{className:"config-label",children:"Anchor"}),e.jsx("span",{className:"config-value",children:n?.anchorUserMessages??"—"})]})]})]})]})}export{E as default};
2
- //# sourceMappingURL=ConfigTab-Cm6ymbmx.js.map
1
+ import{r as i,p as N,j as e,c as w,d as y}from"./index-4gQ7auad.js";const b=[{id:"transparent",label:"Transparent (accents only)",css:{bg:null,fg:"#c9d1d9",accent:"#3fb950",mega:"#f0883e"},ansi:{bg:null,fg:"39",accent:"32",mega:"33"}},{id:"retro",label:"Retro 8-bit green",css:{bg:"#003300",fg:"#33ff33",accent:"#00ff41",mega:"#39ff14"},ansi:{bg:"40",fg:"92",accent:"92",mega:"1;92"}},{id:"orange-bold",label:"Orange bold",css:{bg:"#1a0f00",fg:"#ff8c00",accent:"#ff6b00",mega:"#ff4500"},ansi:{bg:"48;5;52",fg:"38;5;208",accent:"38;5;208",mega:"1;38;5;202"}},{id:"cyan-neon",label:"Cyan neon",css:{bg:"#001a1a",fg:"#00ffff",accent:"#22d3ee",mega:"#7df9ff"},ansi:{bg:"48;5;23",fg:"38;5;51",accent:"38;5;51",mega:"1;38;5;51"}},{id:"amber-mono",label:"Amber phosphor",css:{bg:"#1a1200",fg:"#ffb000",accent:"#ff8c00",mega:"#ff5500"},ansi:{bg:"48;5;58",fg:"38;5;214",accent:"38;5;214",mega:"1;38;5;208"}},{id:"grayscale",label:"Grayscale minimal",css:{bg:"#161616",fg:"#b0b0b0",accent:"#8b949e",mega:"#e6edf3"},ansi:{bg:"48;5;235",fg:"38;5;249",accent:"38;5;249",mega:"1;38;5;255"}}],g=b.map(t=>t.id),c={gameMode:"Turn game mode on/off (themes the widget + dashboard)",theme:"Visual theme (applies instantly)",tuiMode:"TUI widget display density",tier:"Live pressure band — climbs low→mega as context fills the window.",preset:"The env-resolved base compaction preset (low/medium/high/ultra/mega) that set the token threshold.",pressure:"Live pressure = currentTokens / threshold — % of the model context window (threshold fires at the tier's % of window).",threshold:"Compaction threshold = tierPct × model context window — mega-compact trims BELOW pi's native ~80% auto-compact for any model size.",fastGate:"Fast-gate arming floor — the live trim arms once context passes this % of the window."};function T(t,d,l){let o=t.toLocaleString();return d!=null&&l>0&&(o+=` (${Math.round(d*100)}% of ${l.toLocaleString()})`),o}function S(t){return`${Math.round((t||0)*100)}%`}function E(){const[t,d]=i.useState(null),[l,o]=i.useState(null),[m,u]=i.useState(!1);i.useEffect(()=>{let s=!0;const a=async()=>{try{const r=await w();s&&d(r)}catch{}};a();const f=setInterval(()=>void a(),5e3);return()=>{s=!1,clearInterval(f)}},[]),i.useEffect(()=>{let s=!0;const a=async()=>{try{const r=await y();s&&(o(r),r.theme&&(document.documentElement.dataset.theme=r.theme))}catch{}};a();const f=setInterval(()=>void a(),5e3);return()=>{s=!1,clearInterval(f)}},[]);const h=i.useCallback(async s=>{u(!0);try{const a=await N(s);o(a),a.theme&&(document.documentElement.dataset.theme=a.theme)}catch{}u(!1)},[]),x=l?.game_mode_on??!1,p=l?.theme??"transparent",v=l?.tui_display_mode??"full",n=t?.config,j=t?.context.contextWindow??0;return e.jsxs("div",{className:"config-tab",children:[e.jsxs("section",{className:"config-settings",children:[e.jsx("h3",{children:"Settings"}),e.jsx("div",{className:"settings-row",children:e.jsxs("label",{title:c.gameMode,children:[e.jsx("input",{type:"checkbox",checked:x,onChange:s=>h({game_mode_on:s.currentTarget.checked}),disabled:m}),"Game Mode"]})}),e.jsx("div",{className:"settings-row",children:e.jsxs("label",{title:c.theme,children:["Theme",e.jsx("select",{value:p,onChange:s=>h({theme:s.currentTarget.value}),disabled:m,children:b.map(s=>e.jsx("option",{value:s.id,children:s.label},s.id))}),e.jsx("button",{type:"button",disabled:m,className:"theme-cycle-btn",title:"Cycle to the next theme (P3)",onClick:()=>{const s=g.indexOf(p),a=g[(s<0?0:s+1)%g.length];a&&h({theme:a})},children:"Next theme →"})]})}),e.jsx("div",{className:"settings-row",children:e.jsxs("label",{title:c.tuiMode,children:["TUI Mode",e.jsxs("select",{value:v,onChange:s=>h({tui_display_mode:s.currentTarget.value}),disabled:m,children:[e.jsx("option",{value:"full",children:"Full"}),e.jsx("option",{value:"minimal",children:"Minimal"})]})," ",e.jsx("span",{className:"tui-mode-hint",children:"(affects the in-app TUI widget; not shown here)"})]})})]}),e.jsxs("section",{className:"config-display",children:[e.jsx("h3",{children:"Configuration"}),e.jsxs("div",{className:"config-grid",children:[e.jsxs("div",{className:"config-item",children:[e.jsx("span",{className:"config-label",title:c.tier,children:"Tier"}),e.jsx("span",{className:"config-value",children:t?`${t.tier} (live)`:"—"})]}),e.jsxs("div",{className:"config-item",children:[e.jsx("span",{className:"config-label",title:c.preset,children:"Preset"}),e.jsx("span",{className:"config-value",children:t?.presetTier??"—"})]}),e.jsxs("div",{className:"config-item",children:[e.jsx("span",{className:"config-label",title:c.pressure,children:"Pressure"}),e.jsx("span",{className:"config-value",children:t?S(t.pressure):"—"})]}),e.jsxs("div",{className:"config-item",children:[e.jsx("span",{className:"config-label",title:c.threshold,children:"Threshold"}),e.jsx("span",{className:"config-value",children:n?T(n.thresholdTokens,n.tierPct,j):"—"})]}),e.jsxs("div",{className:"config-item",children:[e.jsx("span",{className:"config-label",title:c.fastGate,children:"Fast Gate"}),e.jsx("span",{className:"config-value",children:n?`${n.fastGatePct}%`:"—"})]}),e.jsxs("div",{className:"config-item",children:[e.jsx("span",{className:"config-label",children:"Auto"}),e.jsx("span",{className:"config-value",children:n?n.auto?"enabled":"disabled":"—"})]}),e.jsxs("div",{className:"config-item",children:[e.jsx("span",{className:"config-label",children:"Anchor"}),e.jsx("span",{className:"config-value",children:n?.anchorUserMessages??"—"})]})]})]})]})}export{E as default};
2
+ //# sourceMappingURL=ConfigTab-DDFE-ckA.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ConfigTab-Cm6ymbmx.js","sources":["../../../../src/config/themes.ts","../../src/tabs/ConfigTab.tsx"],"sourcesContent":["/**\n * themes.ts — SINGLE SOURCE OF TRUTH for game-mode visual-effect themes (S30).\n *\n * Each theme defines BOTH an HTML/CSS palette (dashboard) AND an ANSI accent\n * mapping (TUI widget), since the TUI is ANSI and the dashboard is HTML.\n *\n * Theme scope (QA1 correction): themes restyle the mega-compact statusbar\n * widget + the whole dashboard HTML page. We do NOT theme pi's chrome.\n *\n * `transparent` (default) semantics: no background fill, colored accents only —\n * so game-mode stays visible against any terminal background.\n *\n * PREVENT-PI-004: pure config, no network. Pi-agnostic: no pi runtime types.\n */\n\n/** HTML/CSS palette for the dashboard (hex strings, or `null` for \"no bg\"). */\nexport interface ThemeCss {\n /** Page/cell background hex, or `null` for transparent (no bg fill). */\n bg: string | null;\n /** Default foreground (text) hex. */\n fg: string;\n /** Accent hex (bars, highlights, level numbers). */\n accent: string;\n /** MEGA CACHE highlight hex (fires at cache >= 100%). */\n mega: string;\n}\n\n/** ANSI accent mapping for the TUI widget (SGR parameter strings, no `\\x1b[`).\n * `bg` is `null` for transparent themes (no background fill). */\nexport interface ThemeAnsi {\n /** Background SGR params (e.g. \"48;5;23\"), or `null` for no bg fill. */\n bg: string | null;\n /** Foreground SGR params (e.g. \"39\" for default, \"92\" for bright green). */\n fg: string;\n /** Accent SGR params. */\n accent: string;\n /** MEGA CACHE SGR params (fires at cache >= 100%). */\n mega: string;\n}\n\n/** A complete theme = CSS palette + ANSI mapping + display metadata. */\nexport interface Theme {\n id: string;\n label: string;\n css: ThemeCss;\n ansi: ThemeAnsi;\n}\n\nexport const DEFAULT_THEME = \"transparent\";\n\nexport const THEMES: readonly Theme[] = [\n {\n id: \"transparent\",\n label: \"Transparent (accents only)\",\n css: { bg: null, fg: \"#c9d1d9\", accent: \"#3fb950\", mega: \"#f0883e\" },\n ansi: { bg: null, fg: \"39\", accent: \"32\", mega: \"33\" },\n },\n {\n id: \"retro\",\n label: \"Retro 8-bit green\",\n css: { bg: \"#003300\", fg: \"#33ff33\", accent: \"#00ff41\", mega: \"#39ff14\" },\n ansi: { bg: \"40\", fg: \"92\", accent: \"92\", mega: \"1;92\" },\n },\n {\n id: \"orange-bold\",\n label: \"Orange bold\",\n css: { bg: \"#1a0f00\", fg: \"#ff8c00\", accent: \"#ff6b00\", mega: \"#ff4500\" },\n ansi: { bg: \"48;5;52\", fg: \"38;5;208\", accent: \"38;5;208\", mega: \"1;38;5;202\" },\n },\n {\n id: \"cyan-neon\",\n label: \"Cyan neon\",\n css: { bg: \"#001a1a\", fg: \"#00ffff\", accent: \"#22d3ee\", mega: \"#7df9ff\" },\n ansi: { bg: \"48;5;23\", fg: \"38;5;51\", accent: \"38;5;51\", mega: \"1;38;5;51\" },\n },\n {\n id: \"amber-mono\",\n label: \"Amber phosphor\",\n css: { bg: \"#1a1200\", fg: \"#ffb000\", accent: \"#ff8c00\", mega: \"#ff5500\" },\n ansi: { bg: \"48;5;58\", fg: \"38;5;214\", accent: \"38;5;214\", mega: \"1;38;5;208\" },\n },\n {\n id: \"grayscale\",\n label: \"Grayscale minimal\",\n css: { bg: \"#161616\", fg: \"#b0b0b0\", accent: \"#8b949e\", mega: \"#e6edf3\" },\n ansi: { bg: \"48;5;235\", fg: \"38;5;249\", accent: \"38;5;249\", mega: \"1;38;5;255\" },\n },\n] as const;\n\n/** All valid theme ids, in definition order (used for `theme next` cycling). */\nexport const THEME_IDS: readonly string[] = THEMES.map((t) => t.id);\n\n/** Look up a theme by id; returns `undefined` for an unknown id (caller\n * validates + falls back to DEFAULT_THEME). */\nexport function getTheme(id: string): Theme | undefined {\n return THEMES.find((t) => t.id === id);\n}\n\n/** True if `id` is a known theme id. */\nexport function isValidTheme(id: string): boolean {\n return THEME_IDS.includes(id);\n}\n\n/** Cycle to the next theme id (wraps at the end). Falls back to DEFAULT_THEME\n * if `current` is unknown (defensive — the stored id should always be valid). */\nexport function nextTheme(current: string): string {\n const i = THEME_IDS.indexOf(current);\n if (i < 0) return DEFAULT_THEME;\n return THEME_IDS[(i + 1) % THEME_IDS.length]!;\n}\n\n/** CSS variable declarations for a theme's CSS palette. `bg` is `null` for the\n * transparent theme → `--bg: transparent` (no page fill, accents only). Pure,\n * no pi types. */\nexport function themeCssVars(theme: Theme): string {\n const bg = theme.css.bg ?? \"transparent\";\n return `--bg: ${bg}; --fg: ${theme.css.fg}; --accent: ${theme.css.accent}; --mega: ${theme.css.mega};`;\n}\n\n/** A `:root[data-theme=\"<id>\"]` override block carrying the theme's CSS vars,\n * for client-side instant theme switching. Pure, no pi types. */\nexport function themeDataBlock(theme: Theme): string {\n return `:root[data-theme=\"${theme.id}\"]{ ${themeCssVars(theme)} }`;\n}\n","/**\n * dashboard-client/src/tabs/ConfigTab.tsx — Config tab (C3).\n *\n * Full configuration editor:\n * (1) Game mode toggle (checkbox)\n * (2) Theme selector (dropdown) from src/config/themes.ts THEMES array\n * (3) TUI mode selector (Full/Minimal dropdown)\n * (4) Read-only config display section from snapshot (Tier, Preset, Pressure,\n * Threshold, Fast Gate, Auto, Anchor) with verbatim tooltips from html.ts.\n *\n * Uses fetchGameState() to load current state, putGameState() to save changes.\n * Polls gameState and snapshot every 5s. Applies theme by setting\n * document.documentElement.dataset.theme.\n */\n\nimport type React from \"react\";\nimport { useState, useEffect, useCallback } from \"react\";\nimport type { SnapshotResponse, GameStatePatch } from \"@contracts\";\nimport { fetchSnapshot, fetchGameState, putGameState } from \"../api/client\";\nimport { THEMES, THEME_IDS } from \"../../../../src/config/themes\";\n\n/** Actual flat game state returned by the server (the server returns a flat\n * { game_mode_on, theme, tui_display_mode } shape, NOT the contract's nested\n * { config: { enabled, theme, displayMode }, activeRitual } shape). */\ninterface FlatGameState {\n\tgame_mode_on: boolean;\n\ttheme: string;\n\ttui_display_mode: \"full\" | \"minimal\";\n}\ntype FlatGameStatePatch = Partial<FlatGameState>;\n\n/** Extended snapshot config — the runtime includes tierPct (not in contract).\n * Implemented as a type intersection rather than `interface extends` because\n * TypeScript (TS2499) forbids `interface extends T[\"k\"]` indexed-access\n * parents; the resulting type is structurally identical. */\ntype ConfigExt = SnapshotResponse[\"config\"] & {\n\ttierPct?: number | null;\n};\n\n/* Tooltips copied verbatim from extensions/dashboard-server/html.ts */\nconst TOOLTIPS = {\n\tgameMode: \"Turn game mode on/off (themes the widget + dashboard)\",\n\ttheme: \"Visual theme (applies instantly)\",\n\ttuiMode: \"TUI widget display density\",\n\ttier: \"Live pressure band — climbs low\\u2192mega as context fills the window.\",\n\tpreset:\n\t\t\"The env-resolved base compaction preset (low/medium/high/ultra/mega) that set the token threshold.\",\n\tpressure:\n\t\t\"Live pressure = currentTokens / threshold \\u2014 % of the model context window (threshold fires at the tier's % of window).\",\n\tthreshold:\n\t\t\"Compaction threshold = tierPct \\u00d7 model context window \\u2014 mega-compact trims BELOW pi's native ~80% auto-compact for any model size.\",\n\tfastGate:\n\t\t\"Fast-gate arming floor \\u2014 the live trim arms once context passes this % of the window.\",\n} as const;\n\nfunction formatThreshold(\n\ttokens: number,\n\ttierPct: number | null | undefined,\n\tcw: number,\n): string {\n\tlet txt = tokens.toLocaleString();\n\tif (tierPct != null && cw > 0) {\n\t\ttxt += ` (${Math.round(tierPct * 100)}% of ${cw.toLocaleString()})`;\n\t}\n\treturn txt;\n}\n\nfunction formatPressure(pressure: number): string {\n\t/* Runtime sends 0\\u20131 fraction despite contract saying 0\\u2013100. */\n\treturn `${Math.round((pressure || 0) * 100)}%`;\n}\n\nexport default function ConfigTab(): React.ReactElement {\n\tconst [snapshot, setSnapshot] = useState<SnapshotResponse | null>(null);\n\tconst [gameState, setGameState] = useState<FlatGameState | null>(null);\n\tconst [saving, setSaving] = useState(false);\n\n\t/* Poll snapshot every 5s for config display section. */\n\tuseEffect(() => {\n\t\tlet active = true;\n\t\tconst doFetch = async (): Promise<void> => {\n\t\t\ttry {\n\t\t\t\tconst s = await fetchSnapshot();\n\t\t\t\tif (active) setSnapshot(s);\n\t\t\t} catch {\n\t\t\t\t/* non-fatal */\n\t\t\t}\n\t\t};\n\t\tvoid doFetch();\n\t\tconst timer = setInterval(() => void doFetch(), 5000);\n\t\treturn () => {\n\t\t\tactive = false;\n\t\t\tclearInterval(timer);\n\t\t};\n\t}, []);\n\n\t/* Poll game state every 5s. */\n\tuseEffect(() => {\n\t\tlet active = true;\n\t\tconst doFetch = async (): Promise<void> => {\n\t\t\ttry {\n\t\t\t\tconst gs = (await fetchGameState()) as unknown as FlatGameState;\n\t\t\t\tif (active) {\n\t\t\t\t\tsetGameState(gs);\n\t\t\t\t\tif (gs.theme)\n\t\t\t\t\t\tdocument.documentElement.dataset.theme = gs.theme;\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t/* non-fatal */\n\t\t\t}\n\t\t};\n\t\tvoid doFetch();\n\t\tconst timer = setInterval(() => void doFetch(), 5000);\n\t\treturn () => {\n\t\t\tactive = false;\n\t\t\tclearInterval(timer);\n\t\t};\n\t}, []);\n\n\t/* PUT a patch to the server immediately on change. */\n\tconst handlePatch = useCallback(async (patch: FlatGameStatePatch) => {\n\t\tsetSaving(true);\n\t\ttry {\n\t\t\tconst result = (await putGameState(\n\t\t\t\tpatch as unknown as GameStatePatch,\n\t\t\t)) as unknown as FlatGameState;\n\t\t\tsetGameState(result);\n\t\t\tif (result.theme)\n\t\t\t\tdocument.documentElement.dataset.theme = result.theme;\n\t\t} catch {\n\t\t\t/* non-fatal */\n\t\t}\n\t\tsetSaving(false);\n\t}, []);\n\n\tconst gmOn = gameState?.game_mode_on ?? false;\n\tconst themeVal = gameState?.theme ?? \"transparent\";\n\tconst tuiVal = gameState?.tui_display_mode ?? \"full\";\n\n\tconst cfg = snapshot?.config as ConfigExt | undefined;\n\tconst cw = snapshot?.context.contextWindow ?? 0;\n\n\treturn (\n\t\t<div className=\"config-tab\">\n\t\t\t<section className=\"config-settings\">\n\t\t\t\t<h3>Settings</h3>\n\t\t\t\t<div className=\"settings-row\">\n\t\t\t\t\t<label title={TOOLTIPS.gameMode}>\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\t\tchecked={gmOn}\n\t\t\t\t\t\t\tonChange={(e) =>\n\t\t\t\t\t\t\t\thandlePatch({ game_mode_on: e.currentTarget.checked })\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tdisabled={saving}\n\t\t\t\t\t\t/>\n\t\t\t\t\t\tGame Mode\n\t\t\t\t\t</label>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"settings-row\">\n\t\t\t\t\t<label title={TOOLTIPS.theme}>\n\t\t\t\t\t\tTheme\n\t\t\t\t\t\t<select\n\t\t\t\t\t\t\tvalue={themeVal}\n\t\t\t\t\t\t\tonChange={(e) => handlePatch({ theme: e.currentTarget.value })}\n\t\t\t\t\t\t\tdisabled={saving}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{THEMES.map((t) => (\n\t\t\t\t\t\t\t\t<option key={t.id} value={t.id}>\n\t\t\t\t\t\t\t\t\t{t.label}\n\t\t\t\t\t\t\t\t</option>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</select>\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\tdisabled={saving}\n\t\t\t\t\t\t\tclassName=\"theme-cycle-btn\"\n\t\t\t\t\t\t\ttitle=\"Cycle to the next theme (P3)\"\n\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\tconst i = THEME_IDS.indexOf(themeVal);\n\t\t\t\t\t\t\t\tconst next =\n\t\t\t\t\t\t\t\t\tTHEME_IDS[(i < 0 ? 0 : i + 1) % THEME_IDS.length];\n\t\t\t\t\t\t\t\tif (next) void handlePatch({ theme: next });\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\tNext theme →\n\t\t\t\t\t\t</button>\n\t\t\t\t\t</label>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"settings-row\">\n\t\t\t\t\t<label title={TOOLTIPS.tuiMode}>\n\t\t\t\t\t\tTUI Mode\n\t\t\t\t\t\t<select\n\t\t\t\t\t\t\tvalue={tuiVal}\n\t\t\t\t\t\t\tonChange={(e) =>\n\t\t\t\t\t\t\t\thandlePatch({\n\t\t\t\t\t\t\t\t\ttui_display_mode: e.currentTarget.value as \"full\" | \"minimal\",\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tdisabled={saving}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<option value=\"full\">Full</option>\n\t\t\t\t\t\t\t<option value=\"minimal\">Minimal</option>\n\t\t\t\t\t\t</select>{\" \"}\n\t\t\t\t\t\t\t<span className=\"tui-mode-hint\">\n\t\t\t\t\t\t\t\t(affects the in-app TUI widget; not shown here)\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t</label>\n\t\t\t\t</div>\n\t\t\t</section>\n\n\t\t\t<section className=\"config-display\">\n\t\t\t\t<h3>Configuration</h3>\n\t\t\t\t<div className=\"config-grid\">\n\t\t\t\t\t<div className=\"config-item\">\n\t\t\t\t\t\t<span className=\"config-label\" title={TOOLTIPS.tier}>\n\t\t\t\t\t\t\tTier\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span className=\"config-value\">\n\t\t\t\t\t\t\t{snapshot ? `${snapshot.tier} (live)` : \"\\u2014\"}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"config-item\">\n\t\t\t\t\t\t<span className=\"config-label\" title={TOOLTIPS.preset}>\n\t\t\t\t\t\t\tPreset\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span className=\"config-value\">\n\t\t\t\t\t\t\t{snapshot?.presetTier ?? \"\\u2014\"}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"config-item\">\n\t\t\t\t\t\t<span className=\"config-label\" title={TOOLTIPS.pressure}>\n\t\t\t\t\t\t\tPressure\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span className=\"config-value\">\n\t\t\t\t\t\t\t{snapshot ? formatPressure(snapshot.pressure) : \"\\u2014\"}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"config-item\">\n\t\t\t\t\t\t<span className=\"config-label\" title={TOOLTIPS.threshold}>\n\t\t\t\t\t\t\tThreshold\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span className=\"config-value\">\n\t\t\t\t\t\t\t{cfg ? formatThreshold(cfg.thresholdTokens, cfg.tierPct, cw) : \"\\u2014\"}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"config-item\">\n\t\t\t\t\t\t<span className=\"config-label\" title={TOOLTIPS.fastGate}>\n\t\t\t\t\t\t\tFast Gate\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span className=\"config-value\">\n\t\t\t\t\t\t\t{cfg ? `${cfg.fastGatePct}%` : \"\\u2014\"}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"config-item\">\n\t\t\t\t\t\t<span className=\"config-label\">Auto</span>\n\t\t\t\t\t\t<span className=\"config-value\">\n\t\t\t\t\t\t\t{cfg ? (cfg.auto ? \"enabled\" : \"disabled\") : \"\\u2014\"}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"config-item\">\n\t\t\t\t\t\t<span className=\"config-label\">Anchor</span>\n\t\t\t\t\t\t<span className=\"config-value\">\n\t\t\t\t\t\t\t{cfg?.anchorUserMessages ?? \"\\u2014\"}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</section>\n\t\t</div>\n\t);\n}\n"],"names":["THEMES","THEME_IDS","TOOLTIPS","formatThreshold","tokens","tierPct","cw","txt","formatPressure","pressure","ConfigTab","snapshot","setSnapshot","useState","gameState","setGameState","saving","setSaving","useEffect","active","doFetch","s","fetchSnapshot","timer","gs","fetchGameState","handlePatch","useCallback","patch","result","putGameState","gmOn","themeVal","tuiVal","cfg","jsxs","jsx","e","t","i","next"],"mappings":"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"}
1
+ {"version":3,"file":"ConfigTab-DDFE-ckA.js","sources":["../../../../src/config/themes.ts","../../src/tabs/ConfigTab.tsx"],"sourcesContent":["/**\n * themes.ts — SINGLE SOURCE OF TRUTH for game-mode visual-effect themes (S30).\n *\n * Each theme defines BOTH an HTML/CSS palette (dashboard) AND an ANSI accent\n * mapping (TUI widget), since the TUI is ANSI and the dashboard is HTML.\n *\n * Theme scope (QA1 correction): themes restyle the mega-compact statusbar\n * widget + the whole dashboard HTML page. We do NOT theme pi's chrome.\n *\n * `transparent` (default) semantics: no background fill, colored accents only —\n * so game-mode stays visible against any terminal background.\n *\n * PREVENT-PI-004: pure config, no network. Pi-agnostic: no pi runtime types.\n */\n\n/** HTML/CSS palette for the dashboard (hex strings, or `null` for \"no bg\"). */\nexport interface ThemeCss {\n /** Page/cell background hex, or `null` for transparent (no bg fill). */\n bg: string | null;\n /** Default foreground (text) hex. */\n fg: string;\n /** Accent hex (bars, highlights, level numbers). */\n accent: string;\n /** MEGA CACHE highlight hex (fires at cache >= 100%). */\n mega: string;\n}\n\n/** ANSI accent mapping for the TUI widget (SGR parameter strings, no `\\x1b[`).\n * `bg` is `null` for transparent themes (no background fill). */\nexport interface ThemeAnsi {\n /** Background SGR params (e.g. \"48;5;23\"), or `null` for no bg fill. */\n bg: string | null;\n /** Foreground SGR params (e.g. \"39\" for default, \"92\" for bright green). */\n fg: string;\n /** Accent SGR params. */\n accent: string;\n /** MEGA CACHE SGR params (fires at cache >= 100%). */\n mega: string;\n}\n\n/** A complete theme = CSS palette + ANSI mapping + display metadata. */\nexport interface Theme {\n id: string;\n label: string;\n css: ThemeCss;\n ansi: ThemeAnsi;\n}\n\nexport const DEFAULT_THEME = \"transparent\";\n\nexport const THEMES: readonly Theme[] = [\n {\n id: \"transparent\",\n label: \"Transparent (accents only)\",\n css: { bg: null, fg: \"#c9d1d9\", accent: \"#3fb950\", mega: \"#f0883e\" },\n ansi: { bg: null, fg: \"39\", accent: \"32\", mega: \"33\" },\n },\n {\n id: \"retro\",\n label: \"Retro 8-bit green\",\n css: { bg: \"#003300\", fg: \"#33ff33\", accent: \"#00ff41\", mega: \"#39ff14\" },\n ansi: { bg: \"40\", fg: \"92\", accent: \"92\", mega: \"1;92\" },\n },\n {\n id: \"orange-bold\",\n label: \"Orange bold\",\n css: { bg: \"#1a0f00\", fg: \"#ff8c00\", accent: \"#ff6b00\", mega: \"#ff4500\" },\n ansi: { bg: \"48;5;52\", fg: \"38;5;208\", accent: \"38;5;208\", mega: \"1;38;5;202\" },\n },\n {\n id: \"cyan-neon\",\n label: \"Cyan neon\",\n css: { bg: \"#001a1a\", fg: \"#00ffff\", accent: \"#22d3ee\", mega: \"#7df9ff\" },\n ansi: { bg: \"48;5;23\", fg: \"38;5;51\", accent: \"38;5;51\", mega: \"1;38;5;51\" },\n },\n {\n id: \"amber-mono\",\n label: \"Amber phosphor\",\n css: { bg: \"#1a1200\", fg: \"#ffb000\", accent: \"#ff8c00\", mega: \"#ff5500\" },\n ansi: { bg: \"48;5;58\", fg: \"38;5;214\", accent: \"38;5;214\", mega: \"1;38;5;208\" },\n },\n {\n id: \"grayscale\",\n label: \"Grayscale minimal\",\n css: { bg: \"#161616\", fg: \"#b0b0b0\", accent: \"#8b949e\", mega: \"#e6edf3\" },\n ansi: { bg: \"48;5;235\", fg: \"38;5;249\", accent: \"38;5;249\", mega: \"1;38;5;255\" },\n },\n] as const;\n\n/** All valid theme ids, in definition order (used for `theme next` cycling). */\nexport const THEME_IDS: readonly string[] = THEMES.map((t) => t.id);\n\n/** Look up a theme by id; returns `undefined` for an unknown id (caller\n * validates + falls back to DEFAULT_THEME). */\nexport function getTheme(id: string): Theme | undefined {\n return THEMES.find((t) => t.id === id);\n}\n\n/** True if `id` is a known theme id. */\nexport function isValidTheme(id: string): boolean {\n return THEME_IDS.includes(id);\n}\n\n/** Cycle to the next theme id (wraps at the end). Falls back to DEFAULT_THEME\n * if `current` is unknown (defensive — the stored id should always be valid). */\nexport function nextTheme(current: string): string {\n const i = THEME_IDS.indexOf(current);\n if (i < 0) return DEFAULT_THEME;\n return THEME_IDS[(i + 1) % THEME_IDS.length]!;\n}\n\n/** CSS variable declarations for a theme's CSS palette. `bg` is `null` for the\n * transparent theme → `--bg: transparent` (no page fill, accents only). Pure,\n * no pi types. */\nexport function themeCssVars(theme: Theme): string {\n const bg = theme.css.bg ?? \"transparent\";\n return `--bg: ${bg}; --fg: ${theme.css.fg}; --accent: ${theme.css.accent}; --mega: ${theme.css.mega};`;\n}\n\n/** A `:root[data-theme=\"<id>\"]` override block carrying the theme's CSS vars,\n * for client-side instant theme switching. Pure, no pi types. */\nexport function themeDataBlock(theme: Theme): string {\n return `:root[data-theme=\"${theme.id}\"]{ ${themeCssVars(theme)} }`;\n}\n","/**\n * dashboard-client/src/tabs/ConfigTab.tsx — Config tab (C3).\n *\n * Full configuration editor:\n * (1) Game mode toggle (checkbox)\n * (2) Theme selector (dropdown) from src/config/themes.ts THEMES array\n * (3) TUI mode selector (Full/Minimal dropdown)\n * (4) Read-only config display section from snapshot (Tier, Preset, Pressure,\n * Threshold, Fast Gate, Auto, Anchor) with verbatim tooltips from html.ts.\n *\n * Uses fetchGameState() to load current state, putGameState() to save changes.\n * Polls gameState and snapshot every 5s. Applies theme by setting\n * document.documentElement.dataset.theme.\n */\n\nimport type React from \"react\";\nimport { useState, useEffect, useCallback } from \"react\";\nimport type { SnapshotResponse, GameStatePatch } from \"@contracts\";\nimport { fetchSnapshot, fetchGameState, putGameState } from \"../api/client\";\nimport { THEMES, THEME_IDS } from \"../../../../src/config/themes\";\n\n/** Actual flat game state returned by the server (the server returns a flat\n * { game_mode_on, theme, tui_display_mode } shape, NOT the contract's nested\n * { config: { enabled, theme, displayMode }, activeRitual } shape). */\ninterface FlatGameState {\n\tgame_mode_on: boolean;\n\ttheme: string;\n\ttui_display_mode: \"full\" | \"minimal\";\n}\ntype FlatGameStatePatch = Partial<FlatGameState>;\n\n/** Extended snapshot config — the runtime includes tierPct (not in contract).\n * Implemented as a type intersection rather than `interface extends` because\n * TypeScript (TS2499) forbids `interface extends T[\"k\"]` indexed-access\n * parents; the resulting type is structurally identical. */\ntype ConfigExt = SnapshotResponse[\"config\"] & {\n\ttierPct?: number | null;\n};\n\n/* Tooltips copied verbatim from extensions/dashboard-server/html.ts */\nconst TOOLTIPS = {\n\tgameMode: \"Turn game mode on/off (themes the widget + dashboard)\",\n\ttheme: \"Visual theme (applies instantly)\",\n\ttuiMode: \"TUI widget display density\",\n\ttier: \"Live pressure band — climbs low\\u2192mega as context fills the window.\",\n\tpreset:\n\t\t\"The env-resolved base compaction preset (low/medium/high/ultra/mega) that set the token threshold.\",\n\tpressure:\n\t\t\"Live pressure = currentTokens / threshold \\u2014 % of the model context window (threshold fires at the tier's % of window).\",\n\tthreshold:\n\t\t\"Compaction threshold = tierPct \\u00d7 model context window \\u2014 mega-compact trims BELOW pi's native ~80% auto-compact for any model size.\",\n\tfastGate:\n\t\t\"Fast-gate arming floor \\u2014 the live trim arms once context passes this % of the window.\",\n} as const;\n\nfunction formatThreshold(\n\ttokens: number,\n\ttierPct: number | null | undefined,\n\tcw: number,\n): string {\n\tlet txt = tokens.toLocaleString();\n\tif (tierPct != null && cw > 0) {\n\t\ttxt += ` (${Math.round(tierPct * 100)}% of ${cw.toLocaleString()})`;\n\t}\n\treturn txt;\n}\n\nfunction formatPressure(pressure: number): string {\n\t/* Runtime sends 0\\u20131 fraction despite contract saying 0\\u2013100. */\n\treturn `${Math.round((pressure || 0) * 100)}%`;\n}\n\nexport default function ConfigTab(): React.ReactElement {\n\tconst [snapshot, setSnapshot] = useState<SnapshotResponse | null>(null);\n\tconst [gameState, setGameState] = useState<FlatGameState | null>(null);\n\tconst [saving, setSaving] = useState(false);\n\n\t/* Poll snapshot every 5s for config display section. */\n\tuseEffect(() => {\n\t\tlet active = true;\n\t\tconst doFetch = async (): Promise<void> => {\n\t\t\ttry {\n\t\t\t\tconst s = await fetchSnapshot();\n\t\t\t\tif (active) setSnapshot(s);\n\t\t\t} catch {\n\t\t\t\t/* non-fatal */\n\t\t\t}\n\t\t};\n\t\tvoid doFetch();\n\t\tconst timer = setInterval(() => void doFetch(), 5000);\n\t\treturn () => {\n\t\t\tactive = false;\n\t\t\tclearInterval(timer);\n\t\t};\n\t}, []);\n\n\t/* Poll game state every 5s. */\n\tuseEffect(() => {\n\t\tlet active = true;\n\t\tconst doFetch = async (): Promise<void> => {\n\t\t\ttry {\n\t\t\t\tconst gs = (await fetchGameState()) as unknown as FlatGameState;\n\t\t\t\tif (active) {\n\t\t\t\t\tsetGameState(gs);\n\t\t\t\t\tif (gs.theme)\n\t\t\t\t\t\tdocument.documentElement.dataset.theme = gs.theme;\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t/* non-fatal */\n\t\t\t}\n\t\t};\n\t\tvoid doFetch();\n\t\tconst timer = setInterval(() => void doFetch(), 5000);\n\t\treturn () => {\n\t\t\tactive = false;\n\t\t\tclearInterval(timer);\n\t\t};\n\t}, []);\n\n\t/* PUT a patch to the server immediately on change. */\n\tconst handlePatch = useCallback(async (patch: FlatGameStatePatch) => {\n\t\tsetSaving(true);\n\t\ttry {\n\t\t\tconst result = (await putGameState(\n\t\t\t\tpatch as unknown as GameStatePatch,\n\t\t\t)) as unknown as FlatGameState;\n\t\t\tsetGameState(result);\n\t\t\tif (result.theme)\n\t\t\t\tdocument.documentElement.dataset.theme = result.theme;\n\t\t} catch {\n\t\t\t/* non-fatal */\n\t\t}\n\t\tsetSaving(false);\n\t}, []);\n\n\tconst gmOn = gameState?.game_mode_on ?? false;\n\tconst themeVal = gameState?.theme ?? \"transparent\";\n\tconst tuiVal = gameState?.tui_display_mode ?? \"full\";\n\n\tconst cfg = snapshot?.config as ConfigExt | undefined;\n\tconst cw = snapshot?.context.contextWindow ?? 0;\n\n\treturn (\n\t\t<div className=\"config-tab\">\n\t\t\t<section className=\"config-settings\">\n\t\t\t\t<h3>Settings</h3>\n\t\t\t\t<div className=\"settings-row\">\n\t\t\t\t\t<label title={TOOLTIPS.gameMode}>\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\t\tchecked={gmOn}\n\t\t\t\t\t\t\tonChange={(e) =>\n\t\t\t\t\t\t\t\thandlePatch({ game_mode_on: e.currentTarget.checked })\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tdisabled={saving}\n\t\t\t\t\t\t/>\n\t\t\t\t\t\tGame Mode\n\t\t\t\t\t</label>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"settings-row\">\n\t\t\t\t\t<label title={TOOLTIPS.theme}>\n\t\t\t\t\t\tTheme\n\t\t\t\t\t\t<select\n\t\t\t\t\t\t\tvalue={themeVal}\n\t\t\t\t\t\t\tonChange={(e) => handlePatch({ theme: e.currentTarget.value })}\n\t\t\t\t\t\t\tdisabled={saving}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{THEMES.map((t) => (\n\t\t\t\t\t\t\t\t<option key={t.id} value={t.id}>\n\t\t\t\t\t\t\t\t\t{t.label}\n\t\t\t\t\t\t\t\t</option>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</select>\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\tdisabled={saving}\n\t\t\t\t\t\t\tclassName=\"theme-cycle-btn\"\n\t\t\t\t\t\t\ttitle=\"Cycle to the next theme (P3)\"\n\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\tconst i = THEME_IDS.indexOf(themeVal);\n\t\t\t\t\t\t\t\tconst next =\n\t\t\t\t\t\t\t\t\tTHEME_IDS[(i < 0 ? 0 : i + 1) % THEME_IDS.length];\n\t\t\t\t\t\t\t\tif (next) void handlePatch({ theme: next });\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\tNext theme →\n\t\t\t\t\t\t</button>\n\t\t\t\t\t</label>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"settings-row\">\n\t\t\t\t\t<label title={TOOLTIPS.tuiMode}>\n\t\t\t\t\t\tTUI Mode\n\t\t\t\t\t\t<select\n\t\t\t\t\t\t\tvalue={tuiVal}\n\t\t\t\t\t\t\tonChange={(e) =>\n\t\t\t\t\t\t\t\thandlePatch({\n\t\t\t\t\t\t\t\t\ttui_display_mode: e.currentTarget.value as \"full\" | \"minimal\",\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tdisabled={saving}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<option value=\"full\">Full</option>\n\t\t\t\t\t\t\t<option value=\"minimal\">Minimal</option>\n\t\t\t\t\t\t</select>{\" \"}\n\t\t\t\t\t\t\t<span className=\"tui-mode-hint\">\n\t\t\t\t\t\t\t\t(affects the in-app TUI widget; not shown here)\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t</label>\n\t\t\t\t</div>\n\t\t\t</section>\n\n\t\t\t<section className=\"config-display\">\n\t\t\t\t<h3>Configuration</h3>\n\t\t\t\t<div className=\"config-grid\">\n\t\t\t\t\t<div className=\"config-item\">\n\t\t\t\t\t\t<span className=\"config-label\" title={TOOLTIPS.tier}>\n\t\t\t\t\t\t\tTier\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span className=\"config-value\">\n\t\t\t\t\t\t\t{snapshot ? `${snapshot.tier} (live)` : \"\\u2014\"}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"config-item\">\n\t\t\t\t\t\t<span className=\"config-label\" title={TOOLTIPS.preset}>\n\t\t\t\t\t\t\tPreset\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span className=\"config-value\">\n\t\t\t\t\t\t\t{snapshot?.presetTier ?? \"\\u2014\"}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"config-item\">\n\t\t\t\t\t\t<span className=\"config-label\" title={TOOLTIPS.pressure}>\n\t\t\t\t\t\t\tPressure\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span className=\"config-value\">\n\t\t\t\t\t\t\t{snapshot ? formatPressure(snapshot.pressure) : \"\\u2014\"}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"config-item\">\n\t\t\t\t\t\t<span className=\"config-label\" title={TOOLTIPS.threshold}>\n\t\t\t\t\t\t\tThreshold\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span className=\"config-value\">\n\t\t\t\t\t\t\t{cfg ? formatThreshold(cfg.thresholdTokens, cfg.tierPct, cw) : \"\\u2014\"}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"config-item\">\n\t\t\t\t\t\t<span className=\"config-label\" title={TOOLTIPS.fastGate}>\n\t\t\t\t\t\t\tFast Gate\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span className=\"config-value\">\n\t\t\t\t\t\t\t{cfg ? `${cfg.fastGatePct}%` : \"\\u2014\"}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"config-item\">\n\t\t\t\t\t\t<span className=\"config-label\">Auto</span>\n\t\t\t\t\t\t<span className=\"config-value\">\n\t\t\t\t\t\t\t{cfg ? (cfg.auto ? \"enabled\" : \"disabled\") : \"\\u2014\"}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"config-item\">\n\t\t\t\t\t\t<span className=\"config-label\">Anchor</span>\n\t\t\t\t\t\t<span className=\"config-value\">\n\t\t\t\t\t\t\t{cfg?.anchorUserMessages ?? \"\\u2014\"}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</section>\n\t\t</div>\n\t);\n}\n"],"names":["THEMES","THEME_IDS","TOOLTIPS","formatThreshold","tokens","tierPct","cw","txt","formatPressure","pressure","ConfigTab","snapshot","setSnapshot","useState","gameState","setGameState","saving","setSaving","useEffect","active","doFetch","s","fetchSnapshot","timer","gs","fetchGameState","handlePatch","useCallback","patch","result","putGameState","gmOn","themeVal","tuiVal","cfg","jsxs","jsx","e","t","i","next"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import{r as o,j as t}from"./index-BX0bcgLu.js";import{u}from"./useSSE-BBYt5UPB.js";const _={compact_start:"ev-compact",compact_end:"ev-compact",compact_trigger:"ev-trigger",compact_skip:"ev-trigger",tier_changed:"ev-tier",model_changed:"ev-tier",pressure_lifted:"ev-tier",checkpoint_persisted:"ev-checkpoint",recall_inject:"ev-recall",anchors_updated:"ev-recall",config_updated:"ev-config",config_preset:"ev-config",crew_presence_changed:"ev-crew",crew_turn_changed:"ev-crew",crew_bandit_chosen:"ev-crew",session_sample:"ev-recall",game_ritual_start:"ev-game",game_ritual_stage:"ev-game",game_ritual_end:"ev-game",game_mode_changed:"ev-game",game_render:"ev-game"},m=["all","compact_start","compact_end","recall_inject","checkpoint_persisted"];function p(e){switch(e.type){case"compact_start":return`trigger=${e.trigger} session=${e.sessionId.slice(0,8)}`;case"compact_end":return`freed=${e.tokensFreed} ok=${e.success} cp=${e.checkpointId.slice(0,8)}`;case"compact_trigger":return`pressure=${e.pressure}% threshold=${e.threshold}% armed=${e.armed}`;case"compact_skip":return`reason=${e.reason}`;case"tier_changed":return`${e.from} → ${e.to} ctx=${e.contextPct}%`;case"model_changed":return`${e.providerName}/${e.model}`;case"pressure_lifted":return`${e.beforePct}% → ${e.afterPct}%`;case"checkpoint_persisted":return`cp=${e.checkpointId.slice(0,8)} sessionTok=${e.sessionTokens}`;case"recall_inject":return`q="${e.query.slice(0,40)}" chunks=${e.chunks} tok=${e.tokens}`;case"anchors_updated":return`count=${e.count} pinned=${e.pinned}`;case"config_updated":return`key=${e.key}`;case"config_preset":return`preset=${e.preset}`;case"crew_presence_changed":return`agents=${e.activeAgents} turn=${e.currentTurn}`;case"crew_turn_changed":return`turn=${e.turnIndex} agent=${e.agentName}`;case"crew_bandit_chosen":return`agent=${e.chosenAgent} score=${e.score} regret=${e.regret}`;case"session_sample":return`sess=${e.sessionId.slice(0,8)} tok=${e.tokens.toLocaleString()} ctx=${Math.round(e.percent)}%`;case"game_ritual_start":case"game_ritual_stage":case"game_ritual_end":case"game_mode_changed":case"game_render":return`stage=${e.type}`}}function g(e){try{return new Date(e).toLocaleTimeString()}catch{return e}}function h({events:e}){const[s,r]=o.useState("all"),[a,l]=o.useState(null),i=o.useMemo(()=>(s==="all"?e:e.filter(n=>n.type===s)).slice(-200).reverse(),[e,s]);return t.jsxs("div",{className:"event-stream",children:[t.jsxs("div",{className:"event-filters",role:"toolbar",children:[m.map(c=>t.jsx("button",{type:"button",className:`filter-chip ${s===c?"active":""}`,onClick:()=>r(c),children:c==="all"?"all":c.replace(/_/g," ")},c)),t.jsxs("span",{className:"event-count",children:[e.length," buffered"]})]}),t.jsxs("ul",{className:"event-list",children:[i.length===0&&t.jsx("li",{className:"event-empty",children:"No events yet."}),i.map((c,n)=>{const d=_[c.type]??"ev-default";return t.jsxs("li",{className:`event-row ${d}`,onClick:()=>l(a===n?null:n),children:[t.jsx("span",{className:"ev-time",children:g(c.ts)}),t.jsx("span",{className:"ev-type",children:c.type}),t.jsx("span",{className:"ev-summary",children:p(c)}),a===n&&t.jsx("pre",{className:"ev-detail",children:JSON.stringify(c,null,2)})]},`${c.ts}-${n}`)})]})]})}const f=["all","compact","recall","config","crew","game"];function $({active:e,onFilter:s}){return t.jsx("div",{className:"event-category-filter",role:"toolbar","aria-label":"Event category filter",children:f.map(r=>t.jsx("button",{type:"button",className:`category-chip${e===r?" active":""}`,onClick:()=>s(r),"aria-pressed":e===r,children:r},r))})}const x=new Set(["compact_start","compact_end","compact_trigger","compact_skip"]),j=new Set(["config_updated","config_preset","tier_changed","model_changed","pressure_lifted","anchors_updated","checkpoint_persisted"]),k=new Set(["crew_presence_changed","crew_turn_changed","crew_bandit_chosen"]),y=new Set(["game_ritual_start","game_ritual_stage","game_ritual_end","game_mode_changed","game_render"]);function N(e,s){if(s==="all")return!0;const r=e.type;return s==="compact"?x.has(r):s==="recall"?r==="recall_inject":s==="config"?j.has(r):s==="crew"?k.has(r):y.has(r)}function E(){const{events:e,status:s,eventCount:r}=u(),[a,l]=o.useState("all"),i=o.useMemo(()=>e.filter(d=>N(d,a)),[e,a]),c=`sse-status sse-${s}`,n=s==="connected"?"connected":s==="connecting"?"connecting…":s==="error"?"error":"disconnected";return t.jsxs("div",{className:"events-tab",children:[t.jsxs("div",{className:"events-header",children:[t.jsxs("span",{className:c,role:"status",children:[t.jsx("span",{className:"sse-dot","aria-hidden":"true"}),n]}),t.jsxs("span",{className:"event-count",children:[r," events received"]})]}),t.jsx($,{active:a,onFilter:l}),t.jsx(h,{events:i})]})}export{E as default};
2
- //# sourceMappingURL=EventsTab-DXWl_YzI.js.map
1
+ import{r as o,j as t}from"./index-4gQ7auad.js";import{u}from"./useSSE-DNgMuyP6.js";const _={compact_start:"ev-compact",compact_end:"ev-compact",compact_trigger:"ev-trigger",compact_skip:"ev-trigger",tier_changed:"ev-tier",model_changed:"ev-tier",pressure_lifted:"ev-tier",checkpoint_persisted:"ev-checkpoint",recall_inject:"ev-recall",anchors_updated:"ev-recall",config_updated:"ev-config",config_preset:"ev-config",crew_presence_changed:"ev-crew",crew_turn_changed:"ev-crew",crew_bandit_chosen:"ev-crew",session_sample:"ev-recall",game_ritual_start:"ev-game",game_ritual_stage:"ev-game",game_ritual_end:"ev-game",game_mode_changed:"ev-game",game_render:"ev-game"},m=["all","compact_start","compact_end","recall_inject","checkpoint_persisted"];function p(e){switch(e.type){case"compact_start":return`trigger=${e.trigger} session=${e.sessionId.slice(0,8)}`;case"compact_end":return`freed=${e.tokensFreed} ok=${e.success} cp=${e.checkpointId.slice(0,8)}`;case"compact_trigger":return`pressure=${e.pressure}% threshold=${e.threshold}% armed=${e.armed}`;case"compact_skip":return`reason=${e.reason}`;case"tier_changed":return`${e.from} → ${e.to} ctx=${e.contextPct}%`;case"model_changed":return`${e.providerName}/${e.model}`;case"pressure_lifted":return`${e.beforePct}% → ${e.afterPct}%`;case"checkpoint_persisted":return`cp=${e.checkpointId.slice(0,8)} sessionTok=${e.sessionTokens}`;case"recall_inject":return`q="${e.query.slice(0,40)}" chunks=${e.chunks} tok=${e.tokens}`;case"anchors_updated":return`count=${e.count} pinned=${e.pinned}`;case"config_updated":return`key=${e.key}`;case"config_preset":return`preset=${e.preset}`;case"crew_presence_changed":return`agents=${e.activeAgents} turn=${e.currentTurn}`;case"crew_turn_changed":return`turn=${e.turnIndex} agent=${e.agentName}`;case"crew_bandit_chosen":return`agent=${e.chosenAgent} score=${e.score} regret=${e.regret}`;case"session_sample":return`sess=${e.sessionId.slice(0,8)} tok=${e.tokens.toLocaleString()} ctx=${Math.round(e.percent)}%`;case"game_ritual_start":case"game_ritual_stage":case"game_ritual_end":case"game_mode_changed":case"game_render":return`stage=${e.type}`}}function g(e){try{return new Date(e).toLocaleTimeString()}catch{return e}}function h({events:e}){const[s,r]=o.useState("all"),[a,l]=o.useState(null),i=o.useMemo(()=>(s==="all"?e:e.filter(n=>n.type===s)).slice(-200).reverse(),[e,s]);return t.jsxs("div",{className:"event-stream",children:[t.jsxs("div",{className:"event-filters",role:"toolbar",children:[m.map(c=>t.jsx("button",{type:"button",className:`filter-chip ${s===c?"active":""}`,onClick:()=>r(c),children:c==="all"?"all":c.replace(/_/g," ")},c)),t.jsxs("span",{className:"event-count",children:[e.length," buffered"]})]}),t.jsxs("ul",{className:"event-list",children:[i.length===0&&t.jsx("li",{className:"event-empty",children:"No events yet."}),i.map((c,n)=>{const d=_[c.type]??"ev-default";return t.jsxs("li",{className:`event-row ${d}`,onClick:()=>l(a===n?null:n),children:[t.jsx("span",{className:"ev-time",children:g(c.ts)}),t.jsx("span",{className:"ev-type",children:c.type}),t.jsx("span",{className:"ev-summary",children:p(c)}),a===n&&t.jsx("pre",{className:"ev-detail",children:JSON.stringify(c,null,2)})]},`${c.ts}-${n}`)})]})]})}const f=["all","compact","recall","config","crew","game"];function $({active:e,onFilter:s}){return t.jsx("div",{className:"event-category-filter",role:"toolbar","aria-label":"Event category filter",children:f.map(r=>t.jsx("button",{type:"button",className:`category-chip${e===r?" active":""}`,onClick:()=>s(r),"aria-pressed":e===r,children:r},r))})}const x=new Set(["compact_start","compact_end","compact_trigger","compact_skip"]),j=new Set(["config_updated","config_preset","tier_changed","model_changed","pressure_lifted","anchors_updated","checkpoint_persisted"]),k=new Set(["crew_presence_changed","crew_turn_changed","crew_bandit_chosen"]),y=new Set(["game_ritual_start","game_ritual_stage","game_ritual_end","game_mode_changed","game_render"]);function N(e,s){if(s==="all")return!0;const r=e.type;return s==="compact"?x.has(r):s==="recall"?r==="recall_inject":s==="config"?j.has(r):s==="crew"?k.has(r):y.has(r)}function E(){const{events:e,status:s,eventCount:r}=u(),[a,l]=o.useState("all"),i=o.useMemo(()=>e.filter(d=>N(d,a)),[e,a]),c=`sse-status sse-${s}`,n=s==="connected"?"connected":s==="connecting"?"connecting…":s==="error"?"error":"disconnected";return t.jsxs("div",{className:"events-tab",children:[t.jsxs("div",{className:"events-header",children:[t.jsxs("span",{className:c,role:"status",children:[t.jsx("span",{className:"sse-dot","aria-hidden":"true"}),n]}),t.jsxs("span",{className:"event-count",children:[r," events received"]})]}),t.jsx($,{active:a,onFilter:l}),t.jsx(h,{events:i})]})}export{E as default};
2
+ //# sourceMappingURL=EventsTab-B6UxZ7PM.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"EventsTab-DXWl_YzI.js","sources":["../../src/components/EventStream.tsx","../../src/components/EventCategoryFilter.tsx","../../src/tabs/EventsTab.tsx"],"sourcesContent":["/**\n * dashboard-client/src/components/EventStream.tsx — SSE event list with filters.\n *\n * Type badge colored by event type. Timestamp + expandable detail row.\n * Type filter chips (all + per-type). Virtualization: manual windowing for\n * 500+ events (render last N visible).\n */\n\nimport type React from \"react\";\nimport { useState, useMemo } from \"react\";\nimport type { SseEvent } from \"@contracts\";\n\nexport interface EventStreamProps {\n\tevents: SseEvent[];\n}\n\nconst TYPE_COLORS: Record<string, string> = {\n\tcompact_start: \"ev-compact\",\n\tcompact_end: \"ev-compact\",\n\tcompact_trigger: \"ev-trigger\",\n\tcompact_skip: \"ev-trigger\",\n\ttier_changed: \"ev-tier\",\n\tmodel_changed: \"ev-tier\",\n\tpressure_lifted: \"ev-tier\",\n\tcheckpoint_persisted: \"ev-checkpoint\",\n\trecall_inject: \"ev-recall\",\n\tanchors_updated: \"ev-recall\",\n\tconfig_updated: \"ev-config\",\n\tconfig_preset: \"ev-config\",\n\tcrew_presence_changed: \"ev-crew\",\n\tcrew_turn_changed: \"ev-crew\",\n\tcrew_bandit_chosen: \"ev-crew\",\n\tsession_sample: \"ev-recall\",\n\tgame_ritual_start: \"ev-game\",\n\tgame_ritual_stage: \"ev-game\",\n\tgame_ritual_end: \"ev-game\",\n\tgame_mode_changed: \"ev-game\",\n\tgame_render: \"ev-game\",\n};\n\n// Filterable event types (spec: compact_start/end, recall_inject, checkpoint_persisted + all).\nconst FILTER_TYPES = [\n\t\"all\",\n\t\"compact_start\",\n\t\"compact_end\",\n\t\"recall_inject\",\n\t\"checkpoint_persisted\",\n] as const;\ntype FilterType = (typeof FILTER_TYPES)[number];\n\nconst RENDER_WINDOW = 200; // render last 200 to keep DOM light under 500+ events\n\nfunction summarize(ev: SseEvent): string {\n\tswitch (ev.type) {\n\t\tcase \"compact_start\":\n\t\t\treturn `trigger=${ev.trigger} session=${ev.sessionId.slice(0, 8)}`;\n\t\tcase \"compact_end\":\n\t\t\treturn `freed=${ev.tokensFreed} ok=${ev.success} cp=${ev.checkpointId.slice(0, 8)}`;\n\t\tcase \"compact_trigger\":\n\t\t\treturn `pressure=${ev.pressure}% threshold=${ev.threshold}% armed=${ev.armed}`;\n\t\tcase \"compact_skip\":\n\t\t\treturn `reason=${ev.reason}`;\n\t\tcase \"tier_changed\":\n\t\t\treturn `${ev.from} → ${ev.to} ctx=${ev.contextPct}%`;\n\t\tcase \"model_changed\":\n\t\t\treturn `${ev.providerName}/${ev.model}`;\n\t\tcase \"pressure_lifted\":\n\t\t\treturn `${ev.beforePct}% → ${ev.afterPct}%`;\n\t\tcase \"checkpoint_persisted\":\n\t\t\treturn `cp=${ev.checkpointId.slice(0, 8)} sessionTok=${ev.sessionTokens}`;\n\t\tcase \"recall_inject\":\n\t\t\treturn `q=\"${ev.query.slice(0, 40)}\" chunks=${ev.chunks} tok=${ev.tokens}`;\n\t\tcase \"anchors_updated\":\n\t\t\treturn `count=${ev.count} pinned=${ev.pinned}`;\n\t\tcase \"config_updated\":\n\t\t\treturn `key=${ev.key}`;\n\t\tcase \"config_preset\":\n\t\t\treturn `preset=${ev.preset}`;\n\t\tcase \"crew_presence_changed\":\n\t\t\treturn `agents=${ev.activeAgents} turn=${ev.currentTurn}`;\n\t\tcase \"crew_turn_changed\":\n\t\t\treturn `turn=${ev.turnIndex} agent=${ev.agentName}`;\n\t\tcase \"crew_bandit_chosen\":\n\t\t\treturn `agent=${ev.chosenAgent} score=${ev.score} regret=${ev.regret}`;\n\t\tcase \"session_sample\":\n\t\t\treturn `sess=${ev.sessionId.slice(0, 8)} tok=${ev.tokens.toLocaleString()} ctx=${Math.round(ev.percent)}%`;\n\t\tcase \"game_ritual_start\":\n\t\tcase \"game_ritual_stage\":\n\t\tcase \"game_ritual_end\":\n\t\tcase \"game_mode_changed\":\n\t\tcase \"game_render\":\n\t\t\treturn `stage=${ev.type}`;\n\t}\n}\n\nfunction formatTs(ts: string): string {\n\ttry {\n\t\tconst d = new Date(ts);\n\t\treturn d.toLocaleTimeString();\n\t} catch {\n\t\treturn ts;\n\t}\n}\n\nexport function EventStream({ events }: EventStreamProps): React.ReactElement {\n\tconst [filter, setFilter] = useState<FilterType>(\"all\");\n\tconst [expanded, setExpanded] = useState<number | null>(null);\n\n\tconst filtered = useMemo(() => {\n\t\tconst list =\n\t\t\tfilter === \"all\" ? events : events.filter((e) => e.type === filter);\n\t\t// Newest first; render only the last RENDER_WINDOW to keep DOM light.\n\t\treturn list.slice(-RENDER_WINDOW).reverse();\n\t}, [events, filter]);\n\n\treturn (\n\t\t<div className=\"event-stream\">\n\t\t\t<div className=\"event-filters\" role=\"toolbar\">\n\t\t\t\t{FILTER_TYPES.map((t) => (\n\t\t\t\t\t<button\n\t\t\t\t\t\tkey={t}\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tclassName={`filter-chip ${filter === t ? \"active\" : \"\"}`}\n\t\t\t\t\t\tonClick={() => setFilter(t)}\n\t\t\t\t\t>\n\t\t\t\t\t\t{t === \"all\" ? \"all\" : t.replace(/_/g, \" \")}\n\t\t\t\t\t</button>\n\t\t\t\t))}\n\t\t\t\t<span className=\"event-count\">{events.length} buffered</span>\n\t\t\t</div>\n\t\t\t<ul className=\"event-list\">\n\t\t\t\t{filtered.length === 0 && (\n\t\t\t\t\t<li className=\"event-empty\">No events yet.</li>\n\t\t\t\t)}\n\t\t\t\t{filtered.map((ev, idx) => {\n\t\t\t\t\tconst cls = TYPE_COLORS[ev.type] ?? \"ev-default\";\n\t\t\t\t\treturn (\n\t\t\t\t\t\t<li\n\t\t\t\t\t\t\tkey={`${ev.ts}-${idx}`}\n\t\t\t\t\t\t\tclassName={`event-row ${cls}`}\n\t\t\t\t\t\t\tonClick={() => setExpanded(expanded === idx ? null : idx)}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<span className=\"ev-time\">{formatTs(ev.ts)}</span>\n\t\t\t\t\t\t\t<span className=\"ev-type\">{ev.type}</span>\n\t\t\t\t\t\t\t<span className=\"ev-summary\">{summarize(ev)}</span>\n\t\t\t\t\t\t\t{expanded === idx && (\n\t\t\t\t\t\t\t\t<pre className=\"ev-detail\">{JSON.stringify(ev, null, 2)}</pre>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</li>\n\t\t\t\t\t);\n\t\t\t\t})}\n\t\t\t</ul>\n\t\t</div>\n\t);\n}\n","/**\n * dashboard-client/src/components/EventCategoryFilter.tsx\n *\n * Category-level filter chips for the Events tab. Filters SSE events by\n * category: all / compact / recall / config / crew / game.\n */\n\nimport type React from \"react\";\n\nexport type EventCategory =\n\t| \"all\"\n\t| \"compact\"\n\t| \"recall\"\n\t| \"config\"\n\t| \"crew\"\n\t| \"game\";\n\nconst CATEGORIES: readonly EventCategory[] = [\n\t\"all\",\n\t\"compact\",\n\t\"recall\",\n\t\"config\",\n\t\"crew\",\n\t\"game\",\n] as const;\n\nexport interface EventCategoryFilterProps {\n\t/** Currently active category. */\n\tactive: EventCategory;\n\t/** Called when the user selects a category. */\n\tonFilter: (category: EventCategory) => void;\n}\n\nexport function EventCategoryFilter({\n\tactive,\n\tonFilter,\n}: EventCategoryFilterProps): React.ReactElement {\n\treturn (\n\t\t<div className=\"event-category-filter\" role=\"toolbar\" aria-label=\"Event category filter\">\n\t\t\t{CATEGORIES.map((cat) => (\n\t\t\t\t<button\n\t\t\t\t\tkey={cat}\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tclassName={`category-chip${active === cat ? \" active\" : \"\"}`}\n\t\t\t\t\tonClick={() => onFilter(cat)}\n\t\t\t\t\taria-pressed={active === cat}\n\t\t\t\t>\n\t\t\t\t\t{cat}\n\t\t\t\t</button>\n\t\t\t))}\n\t\t</div>\n\t);\n}\n","/**\n * dashboard-client/src/tabs/EventsTab.tsx — Events tab (C1).\n *\n * Live SSE event stream via useSSE + EventStream with category filter chips.\n * Shows connection status + event count. Category filter uses the existing\n * EventCategoryFilter component to filter by all/compact/recall/config/crew/game.\n */\n\nimport type React from \"react\";\nimport { useState, useMemo } from \"react\";\nimport type { SseEvent } from \"@contracts\";\nimport { useSSE } from \"../hooks/useSSE\";\nimport { EventStream } from \"../components/EventStream\";\nimport {\n\tEventCategoryFilter,\n\ttype EventCategory,\n} from \"../components/EventCategoryFilter\";\n\nconst COMPACT_TYPES = new Set([\n\t\"compact_start\",\n\t\"compact_end\",\n\t\"compact_trigger\",\n\t\"compact_skip\",\n]);\nconst CONFIG_TYPES = new Set([\n\t\"config_updated\",\n\t\"config_preset\",\n\t\"tier_changed\",\n\t\"model_changed\",\n\t\"pressure_lifted\",\n\t\"anchors_updated\",\n\t\"checkpoint_persisted\",\n]);\nconst CREW_TYPES = new Set([\n\t\"crew_presence_changed\",\n\t\"crew_turn_changed\",\n\t\"crew_bandit_chosen\",\n]);\nconst GAME_TYPES = new Set([\n\t\"game_ritual_start\",\n\t\"game_ritual_stage\",\n\t\"game_ritual_end\",\n\t\"game_mode_changed\",\n\t\"game_render\",\n]);\n\nfunction matchesCategory(ev: SseEvent, cat: EventCategory): boolean {\n\tif (cat === \"all\") return true;\n\tconst t = ev.type;\n\tif (cat === \"compact\") return COMPACT_TYPES.has(t);\n\tif (cat === \"recall\") return t === \"recall_inject\";\n\tif (cat === \"config\") return CONFIG_TYPES.has(t);\n\tif (cat === \"crew\") return CREW_TYPES.has(t);\n\treturn GAME_TYPES.has(t); // cat === \"game\"\n}\n\nexport default function EventsTab(): React.ReactElement {\n\tconst { events, status, eventCount } = useSSE();\n\tconst [category, setCategory] = useState<EventCategory>(\"all\");\n\n\tconst filtered = useMemo(\n\t\t() => events.filter((e) => matchesCategory(e, category)),\n\t\t[events, category],\n\t);\n\n\tconst statusCls = `sse-status sse-${status}`;\n\tconst statusLabel =\n\t\tstatus === \"connected\"\n\t\t\t? \"connected\"\n\t\t\t: status === \"connecting\"\n\t\t\t\t? \"connecting…\"\n\t\t\t\t: status === \"error\"\n\t\t\t\t\t? \"error\"\n\t\t\t\t\t: \"disconnected\";\n\n\treturn (\n\t\t<div className=\"events-tab\">\n\t\t\t<div className=\"events-header\">\n\t\t\t\t<span className={statusCls} role=\"status\">\n\t\t\t\t\t<span className=\"sse-dot\" aria-hidden=\"true\" />\n\t\t\t\t\t{statusLabel}\n\t\t\t\t</span>\n\t\t\t\t<span className=\"event-count\">{eventCount} events received</span>\n\t\t\t</div>\n\t\t\t<EventCategoryFilter active={category} onFilter={setCategory} />\n\t\t\t<EventStream events={filtered} />\n\t\t</div>\n\t);\n}\n"],"names":["TYPE_COLORS","FILTER_TYPES","summarize","ev","formatTs","ts","EventStream","events","filter","setFilter","useState","expanded","setExpanded","filtered","useMemo","e","jsxs","t","jsx","idx","cls","CATEGORIES","EventCategoryFilter","active","onFilter","cat","COMPACT_TYPES","CONFIG_TYPES","CREW_TYPES","GAME_TYPES","matchesCategory","EventsTab","status","eventCount","useSSE","category","setCategory","statusCls","statusLabel"],"mappings":"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"}
1
+ {"version":3,"file":"EventsTab-B6UxZ7PM.js","sources":["../../src/components/EventStream.tsx","../../src/components/EventCategoryFilter.tsx","../../src/tabs/EventsTab.tsx"],"sourcesContent":["/**\n * dashboard-client/src/components/EventStream.tsx — SSE event list with filters.\n *\n * Type badge colored by event type. Timestamp + expandable detail row.\n * Type filter chips (all + per-type). Virtualization: manual windowing for\n * 500+ events (render last N visible).\n */\n\nimport type React from \"react\";\nimport { useState, useMemo } from \"react\";\nimport type { SseEvent } from \"@contracts\";\n\nexport interface EventStreamProps {\n\tevents: SseEvent[];\n}\n\nconst TYPE_COLORS: Record<string, string> = {\n\tcompact_start: \"ev-compact\",\n\tcompact_end: \"ev-compact\",\n\tcompact_trigger: \"ev-trigger\",\n\tcompact_skip: \"ev-trigger\",\n\ttier_changed: \"ev-tier\",\n\tmodel_changed: \"ev-tier\",\n\tpressure_lifted: \"ev-tier\",\n\tcheckpoint_persisted: \"ev-checkpoint\",\n\trecall_inject: \"ev-recall\",\n\tanchors_updated: \"ev-recall\",\n\tconfig_updated: \"ev-config\",\n\tconfig_preset: \"ev-config\",\n\tcrew_presence_changed: \"ev-crew\",\n\tcrew_turn_changed: \"ev-crew\",\n\tcrew_bandit_chosen: \"ev-crew\",\n\tsession_sample: \"ev-recall\",\n\tgame_ritual_start: \"ev-game\",\n\tgame_ritual_stage: \"ev-game\",\n\tgame_ritual_end: \"ev-game\",\n\tgame_mode_changed: \"ev-game\",\n\tgame_render: \"ev-game\",\n};\n\n// Filterable event types (spec: compact_start/end, recall_inject, checkpoint_persisted + all).\nconst FILTER_TYPES = [\n\t\"all\",\n\t\"compact_start\",\n\t\"compact_end\",\n\t\"recall_inject\",\n\t\"checkpoint_persisted\",\n] as const;\ntype FilterType = (typeof FILTER_TYPES)[number];\n\nconst RENDER_WINDOW = 200; // render last 200 to keep DOM light under 500+ events\n\nfunction summarize(ev: SseEvent): string {\n\tswitch (ev.type) {\n\t\tcase \"compact_start\":\n\t\t\treturn `trigger=${ev.trigger} session=${ev.sessionId.slice(0, 8)}`;\n\t\tcase \"compact_end\":\n\t\t\treturn `freed=${ev.tokensFreed} ok=${ev.success} cp=${ev.checkpointId.slice(0, 8)}`;\n\t\tcase \"compact_trigger\":\n\t\t\treturn `pressure=${ev.pressure}% threshold=${ev.threshold}% armed=${ev.armed}`;\n\t\tcase \"compact_skip\":\n\t\t\treturn `reason=${ev.reason}`;\n\t\tcase \"tier_changed\":\n\t\t\treturn `${ev.from} → ${ev.to} ctx=${ev.contextPct}%`;\n\t\tcase \"model_changed\":\n\t\t\treturn `${ev.providerName}/${ev.model}`;\n\t\tcase \"pressure_lifted\":\n\t\t\treturn `${ev.beforePct}% → ${ev.afterPct}%`;\n\t\tcase \"checkpoint_persisted\":\n\t\t\treturn `cp=${ev.checkpointId.slice(0, 8)} sessionTok=${ev.sessionTokens}`;\n\t\tcase \"recall_inject\":\n\t\t\treturn `q=\"${ev.query.slice(0, 40)}\" chunks=${ev.chunks} tok=${ev.tokens}`;\n\t\tcase \"anchors_updated\":\n\t\t\treturn `count=${ev.count} pinned=${ev.pinned}`;\n\t\tcase \"config_updated\":\n\t\t\treturn `key=${ev.key}`;\n\t\tcase \"config_preset\":\n\t\t\treturn `preset=${ev.preset}`;\n\t\tcase \"crew_presence_changed\":\n\t\t\treturn `agents=${ev.activeAgents} turn=${ev.currentTurn}`;\n\t\tcase \"crew_turn_changed\":\n\t\t\treturn `turn=${ev.turnIndex} agent=${ev.agentName}`;\n\t\tcase \"crew_bandit_chosen\":\n\t\t\treturn `agent=${ev.chosenAgent} score=${ev.score} regret=${ev.regret}`;\n\t\tcase \"session_sample\":\n\t\t\treturn `sess=${ev.sessionId.slice(0, 8)} tok=${ev.tokens.toLocaleString()} ctx=${Math.round(ev.percent)}%`;\n\t\tcase \"game_ritual_start\":\n\t\tcase \"game_ritual_stage\":\n\t\tcase \"game_ritual_end\":\n\t\tcase \"game_mode_changed\":\n\t\tcase \"game_render\":\n\t\t\treturn `stage=${ev.type}`;\n\t}\n}\n\nfunction formatTs(ts: string): string {\n\ttry {\n\t\tconst d = new Date(ts);\n\t\treturn d.toLocaleTimeString();\n\t} catch {\n\t\treturn ts;\n\t}\n}\n\nexport function EventStream({ events }: EventStreamProps): React.ReactElement {\n\tconst [filter, setFilter] = useState<FilterType>(\"all\");\n\tconst [expanded, setExpanded] = useState<number | null>(null);\n\n\tconst filtered = useMemo(() => {\n\t\tconst list =\n\t\t\tfilter === \"all\" ? events : events.filter((e) => e.type === filter);\n\t\t// Newest first; render only the last RENDER_WINDOW to keep DOM light.\n\t\treturn list.slice(-RENDER_WINDOW).reverse();\n\t}, [events, filter]);\n\n\treturn (\n\t\t<div className=\"event-stream\">\n\t\t\t<div className=\"event-filters\" role=\"toolbar\">\n\t\t\t\t{FILTER_TYPES.map((t) => (\n\t\t\t\t\t<button\n\t\t\t\t\t\tkey={t}\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tclassName={`filter-chip ${filter === t ? \"active\" : \"\"}`}\n\t\t\t\t\t\tonClick={() => setFilter(t)}\n\t\t\t\t\t>\n\t\t\t\t\t\t{t === \"all\" ? \"all\" : t.replace(/_/g, \" \")}\n\t\t\t\t\t</button>\n\t\t\t\t))}\n\t\t\t\t<span className=\"event-count\">{events.length} buffered</span>\n\t\t\t</div>\n\t\t\t<ul className=\"event-list\">\n\t\t\t\t{filtered.length === 0 && (\n\t\t\t\t\t<li className=\"event-empty\">No events yet.</li>\n\t\t\t\t)}\n\t\t\t\t{filtered.map((ev, idx) => {\n\t\t\t\t\tconst cls = TYPE_COLORS[ev.type] ?? \"ev-default\";\n\t\t\t\t\treturn (\n\t\t\t\t\t\t<li\n\t\t\t\t\t\t\tkey={`${ev.ts}-${idx}`}\n\t\t\t\t\t\t\tclassName={`event-row ${cls}`}\n\t\t\t\t\t\t\tonClick={() => setExpanded(expanded === idx ? null : idx)}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<span className=\"ev-time\">{formatTs(ev.ts)}</span>\n\t\t\t\t\t\t\t<span className=\"ev-type\">{ev.type}</span>\n\t\t\t\t\t\t\t<span className=\"ev-summary\">{summarize(ev)}</span>\n\t\t\t\t\t\t\t{expanded === idx && (\n\t\t\t\t\t\t\t\t<pre className=\"ev-detail\">{JSON.stringify(ev, null, 2)}</pre>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</li>\n\t\t\t\t\t);\n\t\t\t\t})}\n\t\t\t</ul>\n\t\t</div>\n\t);\n}\n","/**\n * dashboard-client/src/components/EventCategoryFilter.tsx\n *\n * Category-level filter chips for the Events tab. Filters SSE events by\n * category: all / compact / recall / config / crew / game.\n */\n\nimport type React from \"react\";\n\nexport type EventCategory =\n\t| \"all\"\n\t| \"compact\"\n\t| \"recall\"\n\t| \"config\"\n\t| \"crew\"\n\t| \"game\";\n\nconst CATEGORIES: readonly EventCategory[] = [\n\t\"all\",\n\t\"compact\",\n\t\"recall\",\n\t\"config\",\n\t\"crew\",\n\t\"game\",\n] as const;\n\nexport interface EventCategoryFilterProps {\n\t/** Currently active category. */\n\tactive: EventCategory;\n\t/** Called when the user selects a category. */\n\tonFilter: (category: EventCategory) => void;\n}\n\nexport function EventCategoryFilter({\n\tactive,\n\tonFilter,\n}: EventCategoryFilterProps): React.ReactElement {\n\treturn (\n\t\t<div className=\"event-category-filter\" role=\"toolbar\" aria-label=\"Event category filter\">\n\t\t\t{CATEGORIES.map((cat) => (\n\t\t\t\t<button\n\t\t\t\t\tkey={cat}\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tclassName={`category-chip${active === cat ? \" active\" : \"\"}`}\n\t\t\t\t\tonClick={() => onFilter(cat)}\n\t\t\t\t\taria-pressed={active === cat}\n\t\t\t\t>\n\t\t\t\t\t{cat}\n\t\t\t\t</button>\n\t\t\t))}\n\t\t</div>\n\t);\n}\n","/**\n * dashboard-client/src/tabs/EventsTab.tsx — Events tab (C1).\n *\n * Live SSE event stream via useSSE + EventStream with category filter chips.\n * Shows connection status + event count. Category filter uses the existing\n * EventCategoryFilter component to filter by all/compact/recall/config/crew/game.\n */\n\nimport type React from \"react\";\nimport { useState, useMemo } from \"react\";\nimport type { SseEvent } from \"@contracts\";\nimport { useSSE } from \"../hooks/useSSE\";\nimport { EventStream } from \"../components/EventStream\";\nimport {\n\tEventCategoryFilter,\n\ttype EventCategory,\n} from \"../components/EventCategoryFilter\";\n\nconst COMPACT_TYPES = new Set([\n\t\"compact_start\",\n\t\"compact_end\",\n\t\"compact_trigger\",\n\t\"compact_skip\",\n]);\nconst CONFIG_TYPES = new Set([\n\t\"config_updated\",\n\t\"config_preset\",\n\t\"tier_changed\",\n\t\"model_changed\",\n\t\"pressure_lifted\",\n\t\"anchors_updated\",\n\t\"checkpoint_persisted\",\n]);\nconst CREW_TYPES = new Set([\n\t\"crew_presence_changed\",\n\t\"crew_turn_changed\",\n\t\"crew_bandit_chosen\",\n]);\nconst GAME_TYPES = new Set([\n\t\"game_ritual_start\",\n\t\"game_ritual_stage\",\n\t\"game_ritual_end\",\n\t\"game_mode_changed\",\n\t\"game_render\",\n]);\n\nfunction matchesCategory(ev: SseEvent, cat: EventCategory): boolean {\n\tif (cat === \"all\") return true;\n\tconst t = ev.type;\n\tif (cat === \"compact\") return COMPACT_TYPES.has(t);\n\tif (cat === \"recall\") return t === \"recall_inject\";\n\tif (cat === \"config\") return CONFIG_TYPES.has(t);\n\tif (cat === \"crew\") return CREW_TYPES.has(t);\n\treturn GAME_TYPES.has(t); // cat === \"game\"\n}\n\nexport default function EventsTab(): React.ReactElement {\n\tconst { events, status, eventCount } = useSSE();\n\tconst [category, setCategory] = useState<EventCategory>(\"all\");\n\n\tconst filtered = useMemo(\n\t\t() => events.filter((e) => matchesCategory(e, category)),\n\t\t[events, category],\n\t);\n\n\tconst statusCls = `sse-status sse-${status}`;\n\tconst statusLabel =\n\t\tstatus === \"connected\"\n\t\t\t? \"connected\"\n\t\t\t: status === \"connecting\"\n\t\t\t\t? \"connecting…\"\n\t\t\t\t: status === \"error\"\n\t\t\t\t\t? \"error\"\n\t\t\t\t\t: \"disconnected\";\n\n\treturn (\n\t\t<div className=\"events-tab\">\n\t\t\t<div className=\"events-header\">\n\t\t\t\t<span className={statusCls} role=\"status\">\n\t\t\t\t\t<span className=\"sse-dot\" aria-hidden=\"true\" />\n\t\t\t\t\t{statusLabel}\n\t\t\t\t</span>\n\t\t\t\t<span className=\"event-count\">{eventCount} events received</span>\n\t\t\t</div>\n\t\t\t<EventCategoryFilter active={category} onFilter={setCategory} />\n\t\t\t<EventStream events={filtered} />\n\t\t</div>\n\t);\n}\n"],"names":["TYPE_COLORS","FILTER_TYPES","summarize","ev","formatTs","ts","EventStream","events","filter","setFilter","useState","expanded","setExpanded","filtered","useMemo","e","jsxs","t","jsx","idx","cls","CATEGORIES","EventCategoryFilter","active","onFilter","cat","COMPACT_TYPES","CONFIG_TYPES","CREW_TYPES","GAME_TYPES","matchesCategory","EventsTab","status","eventCount","useSSE","category","setCategory","statusCls","statusLabel"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import{r as n,h as m,i as I,j as e,d as $}from"./index-BX0bcgLu.js";import{A as k}from"./AchievementTiles-vEo2MtE2.js";function g(s){return(Math.round(s*10)/10).toString()+"%"}function b(s){return s!=null?new Date(s).toLocaleString():"—"}function M(s){return s&&typeof s=="object"?s:{}}function F(s){return s.split("/").pop()||s}function p({title:s,rows:h,badge:i,renderValue:v}){return e.jsxs("div",{className:"lb-card",children:[e.jsxs("h3",{children:[s,i]}),h.length===0?e.jsx("div",{className:"lb-empty",children:"—"}):e.jsx("table",{children:e.jsx("tbody",{children:h.map((o,x)=>e.jsxs("tr",{children:[e.jsx("td",{title:o.repo_root,children:F(o.repo_root)}),e.jsx("td",{className:"num",children:v(o)})]},`${o.repo_root}-${x}`))})})]})}function B(){const[s,h]=n.useState({}),[i,v]=n.useState(0),[o,x]=n.useState([]),[T,R]=n.useState(!1),[_,G]=n.useState(!0),[w,N]=n.useState(null),j=n.useRef(0),y=n.useCallback(async()=>{try{const[t,a,c,l,D,H]=await Promise.all([m({metric:"cache",limit:25}),m({metric:"dedupe",limit:25}),m({metric:"turns",limit:25}),m({metric:"mega_cache",limit:25}),m({metric:"repos",limit:1}),I()]),E=l.find(f=>f.ts>j.current&&f.value>100);j.current>0&&E&&(N(`oopsie! cache went to ${Math.round(E.value)}% — MEGA CACHE 🥧`),setTimeout(()=>N(null),4e3)),l.length>0&&(j.current=Math.max(...l.map(f=>f.ts))),h({cache:t,dedupe:a,turns:c,mega_cache:l}),v(D[0]?.value??0),x(H)}catch{}finally{G(!1)}},[]);n.useEffect(()=>{let t=!0;const a=async()=>{t&&await y()};a();const c=setInterval(()=>void a(),15e3);return()=>{t=!1,clearInterval(c)}},[y]),n.useEffect(()=>{let t=!0;const a=async()=>{try{const l=await $();t&&(R(!!l.game_mode_on),l.theme&&(document.documentElement.dataset.theme=l.theme))}catch{}};a();const c=setInterval(()=>void a(),15e3);return()=>{t=!1,clearInterval(c)}},[]);const S=s.mega_cache??[];let r=null,d=null;for(const t of S){(r==null||t.value>r)&&(r=t.value);const a=M(t.meta),c=a.firstSeenTs??a.firstSeen??t.ts;(d==null||c<d)&&(d=c)}const A=r!=null&&r>100,u=s.turns??[],L=u.reduce((t,a)=>Math.max(t,a.value),0),V=Math.floor(Math.log2(L+1))+1,C=(s.cache?.length??0)>0||(s.dedupe?.length??0)>0||u.length>0||S.length>0||i>0;return e.jsxs("div",{className:`game-tab${T?" game-mode-on":""}`,children:[w!=null&&e.jsx("div",{className:"mega-cache-toast show",children:w}),A&&e.jsxs("div",{className:"mega-cache-banner",children:["🥧"," MEGA CACHE! peak ",g(r)," — first reached ",b(d)]}),A&&e.jsxs("div",{className:"achievement-tile unlocked",children:["🏆"," Opie's Wild Ride",e.jsxs("span",{className:"ach-detail",children:["best ",g(r)," · first ",b(d)]})]}),e.jsx("h2",{children:"High Scores"}),!C&&!_&&e.jsx("div",{className:"game-empty",children:"No scores yet — run a session with game mode on."}),C&&e.jsxs("div",{className:"game-leaderboards",children:[e.jsx(p,{title:"Cache %",rows:s.cache??[],renderValue:t=>g(t.value)}),e.jsx(p,{title:"Dedupe (collapsed)",rows:s.dedupe??[],renderValue:t=>t.value.toLocaleString()}),e.jsx(p,{title:"Turns",rows:u,badge:u.length>0?e.jsxs("span",{className:"lvl-badge",children:["LVL ",V]}):void 0,renderValue:t=>t.value.toLocaleString()}),e.jsx(p,{title:"MEGA CACHE trophies",rows:S,renderValue:t=>e.jsxs(e.Fragment,{children:[g(t.value),e.jsx("span",{className:"lb-meta",children:b(M(t.meta).firstSeenTs??M(t.meta).firstSeen??t.ts)})]})})]}),i>0&&e.jsx("div",{className:"repos-badge-line",children:e.jsxs("span",{className:"repos-badge",children:[i," repos"]})}),e.jsx("h3",{children:"Achievements"}),e.jsx(k,{achievements:o})]})}export{B as default};
2
- //# sourceMappingURL=GameTab-BLDudcO5.js.map
1
+ import{r as n,k as m,l as H,j as e,d as I}from"./index-4gQ7auad.js";import{A as $}from"./AchievementTiles-BoL7qBOJ.js";function g(s){return(Math.round(s*10)/10).toString()+"%"}function b(s){return s!=null?new Date(s).toLocaleString():"—"}function M(s){return s&&typeof s=="object"?s:{}}function F(s){return s.split("/").pop()||s}function p({title:s,rows:u,badge:i,renderValue:v}){return e.jsxs("div",{className:"lb-card",children:[e.jsxs("h3",{children:[s,i]}),u.length===0?e.jsx("div",{className:"lb-empty",children:"—"}):e.jsx("table",{children:e.jsx("tbody",{children:u.map((o,x)=>e.jsxs("tr",{children:[e.jsx("td",{title:o.repo_root,children:F(o.repo_root)}),e.jsx("td",{className:"num",children:v(o)})]},`${o.repo_root}-${x}`))})})]})}function B(){const[s,u]=n.useState({}),[i,v]=n.useState(0),[o,x]=n.useState([]),[T,R]=n.useState(!1),[_,G]=n.useState(!0),[w,N]=n.useState(null),j=n.useRef(0),y=n.useCallback(async()=>{try{const[t,a,c,l,k,D]=await Promise.all([m({metric:"cache",limit:25}),m({metric:"dedupe",limit:25}),m({metric:"turns",limit:25}),m({metric:"mega_cache",limit:25}),m({metric:"repos",limit:1}),H()]),E=l.find(f=>f.ts>j.current&&f.value>100);j.current>0&&E&&(N(`oopsie! cache went to ${Math.round(E.value)}% — MEGA CACHE 🥧`),setTimeout(()=>N(null),4e3)),l.length>0&&(j.current=Math.max(...l.map(f=>f.ts))),u({cache:t,dedupe:a,turns:c,mega_cache:l}),v(k[0]?.value??0),x(D)}catch{}finally{G(!1)}},[]);n.useEffect(()=>{let t=!0;const a=async()=>{t&&await y()};a();const c=setInterval(()=>void a(),15e3);return()=>{t=!1,clearInterval(c)}},[y]),n.useEffect(()=>{let t=!0;const a=async()=>{try{const l=await I();t&&(R(!!l.game_mode_on),l.theme&&(document.documentElement.dataset.theme=l.theme))}catch{}};a();const c=setInterval(()=>void a(),15e3);return()=>{t=!1,clearInterval(c)}},[]);const S=s.mega_cache??[];let r=null,d=null;for(const t of S){(r==null||t.value>r)&&(r=t.value);const a=M(t.meta),c=a.firstSeenTs??a.firstSeen??t.ts;(d==null||c<d)&&(d=c)}const A=r!=null&&r>100,h=s.turns??[],L=h.reduce((t,a)=>Math.max(t,a.value),0),V=Math.floor(Math.log2(L+1))+1,C=(s.cache?.length??0)>0||(s.dedupe?.length??0)>0||h.length>0||S.length>0||i>0;return e.jsxs("div",{className:`game-tab${T?" game-mode-on":""}`,children:[w!=null&&e.jsx("div",{className:"mega-cache-toast show",children:w}),A&&e.jsxs("div",{className:"mega-cache-banner",children:["🥧"," MEGA CACHE! peak ",g(r)," — first reached ",b(d)]}),A&&e.jsxs("div",{className:"achievement-tile unlocked",children:["🏆"," Opie's Wild Ride",e.jsxs("span",{className:"ach-detail",children:["best ",g(r)," · first ",b(d)]})]}),e.jsx("h2",{children:"High Scores"}),!C&&!_&&e.jsx("div",{className:"game-empty",children:"No scores yet — run a session with game mode on."}),C&&e.jsxs("div",{className:"game-leaderboards",children:[e.jsx(p,{title:"Cache %",rows:s.cache??[],renderValue:t=>g(t.value)}),e.jsx(p,{title:"Dedupe (collapsed)",rows:s.dedupe??[],renderValue:t=>t.value.toLocaleString()}),e.jsx(p,{title:"Turns",rows:h,badge:h.length>0?e.jsxs("span",{className:"lvl-badge",children:["LVL ",V]}):void 0,renderValue:t=>t.value.toLocaleString()}),e.jsx(p,{title:"MEGA CACHE trophies",rows:S,renderValue:t=>e.jsxs(e.Fragment,{children:[g(t.value),e.jsx("span",{className:"lb-meta",children:b(M(t.meta).firstSeenTs??M(t.meta).firstSeen??t.ts)})]})})]}),i>0&&e.jsx("div",{className:"repos-badge-line",children:e.jsxs("span",{className:"repos-badge",children:[i," repos"]})}),e.jsx("h3",{children:"Achievements"}),e.jsx($,{achievements:o})]})}export{B as default};
2
+ //# sourceMappingURL=GameTab-DehUX-KE.js.map
@@ -1 +1 @@
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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"}
1
+ {"version":3,"file":"GameTab-DehUX-KE.js","sources":["../../src/tabs/GameTab.tsx"],"sourcesContent":["/**\n * dashboard-client/src/tabs/GameTab.tsx — Game tab (NEW).\n *\n * Renders:\n * (1) MEGA CACHE banner (animated gradient, shown when any mega_cache > 100)\n * (2) Opie unlock tile (shown when mega_cache > 100)\n * (3) High Scores header\n * (4) 4 leaderboards in 2×2 grid: Cache %, Dedupe, Turns (LVL badge),\n * MEGA CACHE trophies (firstSeen date meta)\n * (5) Repos count badge via fetchGameScores({metric:'repos',limit:1})\n * (6) Achievements sub-section (AchievementTiles shared component)\n * (7) Game empty state\n * Polls scores + achievements every 15s, game-state every 15s.\n * Applies data-theme accent vars when game_mode_on.\n */\n\nimport type React from \"react\";\nimport { useState, useEffect, useCallback, useRef } from \"react\";\nimport type { GameScoreRow, AchievementRow } from \"@contracts\";\nimport { fetchGameScores, fetchAchievements, fetchGameState } from \"../api/client\";\nimport { AchievementTiles } from \"../components/AchievementTiles\";\n\n/** Actual flat game state shape from the server (NOT the nested contract). */\ninterface FlatGameState {\n\tgame_mode_on: boolean;\n\ttheme: string;\n\ttui_display_mode: \"full\" | \"minimal\";\n}\n\n/** Metadata object inside a mega_cache GameScoreRow. */\ninterface TrophyMeta {\n\tfirstSeenTs?: number;\n\tfirstSeen?: number;\n}\n\n/* ── Format helpers (matching html.ts logic) ─────────────────────────── */\n\nfunction fmtPct(v: number): string {\n\treturn (Math.round(v * 10) / 10).toString() + \"%\";\n}\n\nfunction fmtDate(ts: number | null | undefined): string {\n\treturn ts != null ? new Date(ts).toLocaleString() : \"\\u2014\";\n}\n\nfunction trophyMeta(m: unknown): TrophyMeta {\n\treturn m && typeof m === \"object\" ? (m as TrophyMeta) : {};\n}\n\nfunction repoBasename(path: string): string {\n\treturn path.split(\"/\").pop() || path;\n}\n\n/* ── Leaderboard card ────────────────────────────────────────────────── */\n\ninterface LeaderboardCardProps {\n\ttitle: string;\n\trows: GameScoreRow[];\n\tbadge?: React.ReactNode;\n\trenderValue: (r: GameScoreRow) => React.ReactNode;\n}\n\nfunction LeaderboardCard({\n\ttitle,\n\trows,\n\tbadge,\n\trenderValue,\n}: LeaderboardCardProps): React.ReactElement {\n\treturn (\n\t\t<div className=\"lb-card\">\n\t\t\t<h3>\n\t\t\t\t{title}\n\t\t\t\t{badge}\n\t\t\t</h3>\n\t\t\t{rows.length === 0 ? (\n\t\t\t\t<div className=\"lb-empty\">—</div>\n\t\t\t) : (\n\t\t\t\t<table>\n\t\t\t\t\t<tbody>\n\t\t\t\t\t\t{rows.map((r, i) => (\n\t\t\t\t\t\t\t<tr key={`${r.repo_root}-${i}`}>\n\t\t\t\t\t\t\t\t<td title={r.repo_root}>{repoBasename(r.repo_root)}</td>\n\t\t\t\t\t\t\t\t<td className=\"num\">{renderValue(r)}</td>\n\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</tbody>\n\t\t\t\t</table>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n\n/* ── Main GameTab component ──────────────────────────────────────────── */\n\nexport default function GameTab(): React.ReactElement {\n\tconst [scores, setScores] = useState<\n\t\tRecord<string, GameScoreRow[]>\n\t>({});\n\tconst [reposCount, setReposCount] = useState(0);\n\tconst [achievements, setAchievements] = useState<AchievementRow[]>([]);\n\tconst [gameModeOn, setGameModeOn] = useState(false);\n\tconst [loading, setLoading] = useState(true);\n\tconst [megaToast, setMegaToast] = useState<string | null>(null);\n\tconst lastMegaTsRef = useRef(0);\n\n\tconst fetchAll = useCallback(async (): Promise<void> => {\n\t\ttry {\n\t\t\tconst [cache, dedupe, turns, megaCache, repos, ach] =\n\t\t\t\tawait Promise.all([\n\t\t\t\t\tfetchGameScores({ metric: \"cache\", limit: 25 }),\n\t\t\t\t\tfetchGameScores({ metric: \"dedupe\", limit: 25 }),\n\t\t\t\t\tfetchGameScores({ metric: \"turns\", limit: 25 }),\n\t\t\t\t\tfetchGameScores({ metric: \"mega_cache\", limit: 25 }),\n\t\t\t\t\tfetchGameScores({ metric: \"repos\", limit: 1 }),\n\t\t\t\t\tfetchAchievements(),\n\t\t\t\t]);\n\n\t\t\t/* Mega-cache toast: new row with value > 100 since last poll */\n\t\t\tconst newRow = megaCache.find(\n\t\t\t\t(r) => r.ts > lastMegaTsRef.current && r.value > 100,\n\t\t\t);\n\t\t\tif (lastMegaTsRef.current > 0 && newRow) {\n\t\t\t\tsetMegaToast(\n\t\t\t\t\t`oopsie! cache went to ${Math.round(newRow.value)}% — MEGA CACHE \\uD83E\\uDD67`,\n\t\t\t\t);\n\t\t\t\tsetTimeout(() => setMegaToast(null), 4000);\n\t\t\t}\n\t\t\tif (megaCache.length > 0) {\n\t\t\t\tlastMegaTsRef.current = Math.max(\n\t\t\t\t\t...megaCache.map((r) => r.ts),\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tsetScores({ cache, dedupe, turns, mega_cache: megaCache });\n\t\t\tsetReposCount(repos[0]?.value ?? 0);\n\t\t\tsetAchievements(ach);\n\t\t} catch {\n\t\t\t/* non-fatal */\n\t\t} finally {\n\t\t\tsetLoading(false);\n\t\t}\n\t}, []);\n\n\t/* Poll scores + achievements every 15s. */\n\tuseEffect(() => {\n\t\tlet active = true;\n\t\tconst doFetch = async (): Promise<void> => {\n\t\t\tif (active) await fetchAll();\n\t\t};\n\t\tvoid doFetch();\n\t\tconst timer = setInterval(() => void doFetch(), 15000);\n\t\treturn () => {\n\t\t\tactive = false;\n\t\t\tclearInterval(timer);\n\t\t};\n\t}, [fetchAll]);\n\n\t/* Poll game state every 15s for styling. */\n\tuseEffect(() => {\n\t\tlet active = true;\n\t\tconst doFetch = async (): Promise<void> => {\n\t\t\ttry {\n\t\t\t\tconst gs = (await fetchGameState()) as unknown as FlatGameState;\n\t\t\t\tif (active) {\n\t\t\t\t\tsetGameModeOn(!!gs.game_mode_on);\n\t\t\t\t\tif (gs.theme)\n\t\t\t\t\t\tdocument.documentElement.dataset.theme = gs.theme;\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t/* non-fatal */\n\t\t\t}\n\t\t};\n\t\tvoid doFetch();\n\t\tconst timer = setInterval(() => void doFetch(), 15000);\n\t\treturn () => {\n\t\t\tactive = false;\n\t\t\tclearInterval(timer);\n\t\t};\n\t}, []);\n\n\t/* Compute mega-cache banner data */\n\tconst megaRows = scores.mega_cache ?? [];\n\tlet bestMega: number | null = null;\n\tlet firstSeen: number | null = null;\n\tfor (const r of megaRows) {\n\t\tif (bestMega == null || r.value > bestMega) bestMega = r.value;\n\t\tconst m = trophyMeta(r.meta);\n\t\tconst fs = m.firstSeenTs ?? m.firstSeen ?? r.ts;\n\t\tif (firstSeen == null || fs < firstSeen) firstSeen = fs;\n\t}\n\tconst showMega = bestMega != null && bestMega > 100;\n\n\t/* Turns level badge */\n\tconst turnsRows = scores.turns ?? [];\n\tconst maxTurns = turnsRows.reduce((mx, r) => Math.max(mx, r.value), 0);\n\tconst lvl = Math.floor(Math.log2(maxTurns + 1)) + 1;\n\n\t/* Game empty state */\n\tconst hasData =\n\t\t(scores.cache?.length ?? 0) > 0 ||\n\t\t(scores.dedupe?.length ?? 0) > 0 ||\n\t\tturnsRows.length > 0 ||\n\t\tmegaRows.length > 0 ||\n\t\treposCount > 0;\n\n\treturn (\n\t\t<div className={`game-tab${gameModeOn ? \" game-mode-on\" : \"\"}`}>\n\t\t\t{megaToast != null && (\n\t\t\t\t<div className=\"mega-cache-toast show\">{megaToast}</div>\n\t\t\t)}\n\n\t\t\t{showMega && (\n\t\t\t\t<div className=\"mega-cache-banner\">\n\t\t\t\t\t{\"\\uD83E\\uDD67\"} MEGA CACHE! peak {fmtPct(bestMega!)} — first\n\t\t\t\t\treached {fmtDate(firstSeen)}\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{showMega && (\n\t\t\t\t<div className=\"achievement-tile unlocked\">\n\t\t\t\t\t{\"\\uD83C\\uDFC6\"} Opie's Wild Ride\n\t\t\t\t\t<span className=\"ach-detail\">\n\t\t\t\t\t\tbest {fmtPct(bestMega!)} · first {fmtDate(firstSeen)}\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t<h2>High Scores</h2>\n\n\t\t\t{!hasData && !loading && (\n\t\t\t\t<div className=\"game-empty\">\n\t\t\t\t\tNo scores yet — run a session with game mode on.\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{hasData && (\n\t\t\t\t<div className=\"game-leaderboards\">\n\t\t\t\t\t<LeaderboardCard\n\t\t\t\t\t\ttitle=\"Cache %\"\n\t\t\t\t\t\trows={scores.cache ?? []}\n\t\t\t\t\t\trenderValue={(r) => fmtPct(r.value)}\n\t\t\t\t\t/>\n\t\t\t\t\t<LeaderboardCard\n\t\t\t\t\t\ttitle=\"Dedupe (collapsed)\"\n\t\t\t\t\t\trows={scores.dedupe ?? []}\n\t\t\t\t\t\trenderValue={(r) => r.value.toLocaleString()}\n\t\t\t\t\t/>\n\t\t\t\t\t<LeaderboardCard\n\t\t\t\t\t\ttitle=\"Turns\"\n\t\t\t\t\t\trows={turnsRows}\n\t\t\t\t\t\tbadge={\n\t\t\t\t\t\t\tturnsRows.length > 0 ? (\n\t\t\t\t\t\t\t\t<span className=\"lvl-badge\">LVL {lvl}</span>\n\t\t\t\t\t\t\t) : undefined\n\t\t\t\t\t\t}\n\t\t\t\t\t\trenderValue={(r) => r.value.toLocaleString()}\n\t\t\t\t\t/>\n\t\t\t\t\t<LeaderboardCard\n\t\t\t\t\t\ttitle=\"MEGA CACHE trophies\"\n\t\t\t\t\t\trows={megaRows}\n\t\t\t\t\t\trenderValue={(r) => (\n\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t{fmtPct(r.value)}\n\t\t\t\t\t\t\t\t<span className=\"lb-meta\">\n\t\t\t\t\t\t\t\t\t{fmtDate(\n\t\t\t\t\t\t\t\t\t\ttrophyMeta(r.meta).firstSeenTs ??\n\t\t\t\t\t\t\t\t\t\t\ttrophyMeta(r.meta).firstSeen ??\n\t\t\t\t\t\t\t\t\t\t\tr.ts,\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t)}\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{reposCount > 0 && (\n\t\t\t\t<div className=\"repos-badge-line\">\n\t\t\t\t\t<span className=\"repos-badge\">{reposCount} repos</span>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t<h3>Achievements</h3>\n\t\t\t<AchievementTiles achievements={achievements} />\n\t\t</div>\n\t);\n}\n"],"names":["fmtPct","v","fmtDate","ts","trophyMeta","m","repoBasename","path","LeaderboardCard","title","rows","badge","renderValue","jsxs","jsx","r","i","GameTab","scores","setScores","useState","reposCount","setReposCount","achievements","setAchievements","gameModeOn","setGameModeOn","loading","setLoading","megaToast","setMegaToast","lastMegaTsRef","useRef","fetchAll","useCallback","cache","dedupe","turns","megaCache","repos","ach","fetchGameScores","fetchAchievements","newRow","useEffect","active","doFetch","timer","gs","fetchGameState","megaRows","bestMega","firstSeen","fs","showMega","turnsRows","maxTurns","mx","lvl","hasData","Fragment","AchievementTiles"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import{u as x,r as d,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
1
+ import{u as x,r as d,z as j,A as g,j as e,B as b}from"./index-4gQ7auad.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-0GWRt4wn.js.map
@@ -1 +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
+ {"version":3,"file":"MaintenanceTab-0GWRt4wn.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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"}