@opensip-cli/dashboard 0.1.0

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 (309) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +8 -0
  3. package/README.md +31 -0
  4. package/dist/__tests__/catalog-provenance.test.d.ts +11 -0
  5. package/dist/__tests__/catalog-provenance.test.d.ts.map +1 -0
  6. package/dist/__tests__/catalog-provenance.test.js +130 -0
  7. package/dist/__tests__/catalog-provenance.test.js.map +1 -0
  8. package/dist/__tests__/coupling-attribution.test.d.ts +9 -0
  9. package/dist/__tests__/coupling-attribution.test.d.ts.map +1 -0
  10. package/dist/__tests__/coupling-attribution.test.js +99 -0
  11. package/dist/__tests__/coupling-attribution.test.js.map +1 -0
  12. package/dist/__tests__/dashboard-bundle-weight.test.d.ts +16 -0
  13. package/dist/__tests__/dashboard-bundle-weight.test.d.ts.map +1 -0
  14. package/dist/__tests__/dashboard-bundle-weight.test.js +73 -0
  15. package/dist/__tests__/dashboard-bundle-weight.test.js.map +1 -0
  16. package/dist/__tests__/dashboard-cell-containment.test.d.ts +2 -0
  17. package/dist/__tests__/dashboard-cell-containment.test.d.ts.map +1 -0
  18. package/dist/__tests__/dashboard-cell-containment.test.js +49 -0
  19. package/dist/__tests__/dashboard-cell-containment.test.js.map +1 -0
  20. package/dist/__tests__/dashboard-cytoscape-vendor.test.d.ts +10 -0
  21. package/dist/__tests__/dashboard-cytoscape-vendor.test.d.ts.map +1 -0
  22. package/dist/__tests__/dashboard-cytoscape-vendor.test.js +40 -0
  23. package/dist/__tests__/dashboard-cytoscape-vendor.test.js.map +1 -0
  24. package/dist/__tests__/dashboard-editor-link.test.d.ts +5 -0
  25. package/dist/__tests__/dashboard-editor-link.test.d.ts.map +1 -0
  26. package/dist/__tests__/dashboard-editor-link.test.js +36 -0
  27. package/dist/__tests__/dashboard-editor-link.test.js.map +1 -0
  28. package/dist/__tests__/dashboard-filters.test.d.ts +9 -0
  29. package/dist/__tests__/dashboard-filters.test.d.ts.map +1 -0
  30. package/dist/__tests__/dashboard-filters.test.js +95 -0
  31. package/dist/__tests__/dashboard-filters.test.js.map +1 -0
  32. package/dist/__tests__/dashboard-function-card-singleton.test.d.ts +9 -0
  33. package/dist/__tests__/dashboard-function-card-singleton.test.d.ts.map +1 -0
  34. package/dist/__tests__/dashboard-function-card-singleton.test.js +87 -0
  35. package/dist/__tests__/dashboard-function-card-singleton.test.js.map +1 -0
  36. package/dist/__tests__/dashboard-function-card.test.d.ts +10 -0
  37. package/dist/__tests__/dashboard-function-card.test.d.ts.map +1 -0
  38. package/dist/__tests__/dashboard-function-card.test.js +403 -0
  39. package/dist/__tests__/dashboard-function-card.test.js.map +1 -0
  40. package/dist/__tests__/dashboard-function-row.test.d.ts +11 -0
  41. package/dist/__tests__/dashboard-function-row.test.d.ts.map +1 -0
  42. package/dist/__tests__/dashboard-function-row.test.js +101 -0
  43. package/dist/__tests__/dashboard-function-row.test.js.map +1 -0
  44. package/dist/__tests__/dashboard-generator-graph-catalog.test.d.ts +9 -0
  45. package/dist/__tests__/dashboard-generator-graph-catalog.test.d.ts.map +1 -0
  46. package/dist/__tests__/dashboard-generator-graph-catalog.test.js +75 -0
  47. package/dist/__tests__/dashboard-generator-graph-catalog.test.js.map +1 -0
  48. package/dist/__tests__/dashboard-graph-offline.integration.test.d.ts +13 -0
  49. package/dist/__tests__/dashboard-graph-offline.integration.test.d.ts.map +1 -0
  50. package/dist/__tests__/dashboard-graph-offline.integration.test.js +55 -0
  51. package/dist/__tests__/dashboard-graph-offline.integration.test.js.map +1 -0
  52. package/dist/__tests__/dashboard-graph-scc.test.d.ts +17 -0
  53. package/dist/__tests__/dashboard-graph-scc.test.d.ts.map +1 -0
  54. package/dist/__tests__/dashboard-graph-scc.test.js +89 -0
  55. package/dist/__tests__/dashboard-graph-scc.test.js.map +1 -0
  56. package/dist/__tests__/dashboard-graph-view-model.test.d.ts +12 -0
  57. package/dist/__tests__/dashboard-graph-view-model.test.d.ts.map +1 -0
  58. package/dist/__tests__/dashboard-graph-view-model.test.js +331 -0
  59. package/dist/__tests__/dashboard-graph-view-model.test.js.map +1 -0
  60. package/dist/__tests__/dashboard-help-drawer.test.d.ts +9 -0
  61. package/dist/__tests__/dashboard-help-drawer.test.d.ts.map +1 -0
  62. package/dist/__tests__/dashboard-help-drawer.test.js +101 -0
  63. package/dist/__tests__/dashboard-help-drawer.test.js.map +1 -0
  64. package/dist/__tests__/dashboard-html.test.d.ts +2 -0
  65. package/dist/__tests__/dashboard-html.test.d.ts.map +1 -0
  66. package/dist/__tests__/dashboard-html.test.js +169 -0
  67. package/dist/__tests__/dashboard-html.test.js.map +1 -0
  68. package/dist/__tests__/dashboard-indexes.test.d.ts +9 -0
  69. package/dist/__tests__/dashboard-indexes.test.d.ts.map +1 -0
  70. package/dist/__tests__/dashboard-indexes.test.js +211 -0
  71. package/dist/__tests__/dashboard-indexes.test.js.map +1 -0
  72. package/dist/__tests__/dashboard-path-utils.test.d.ts +6 -0
  73. package/dist/__tests__/dashboard-path-utils.test.d.ts.map +1 -0
  74. package/dist/__tests__/dashboard-path-utils.test.js +54 -0
  75. package/dist/__tests__/dashboard-path-utils.test.js.map +1 -0
  76. package/dist/__tests__/dashboard-search.test.d.ts +5 -0
  77. package/dist/__tests__/dashboard-search.test.d.ts.map +1 -0
  78. package/dist/__tests__/dashboard-search.test.js +46 -0
  79. package/dist/__tests__/dashboard-search.test.js.map +1 -0
  80. package/dist/__tests__/dashboard-sessions.test.d.ts +14 -0
  81. package/dist/__tests__/dashboard-sessions.test.d.ts.map +1 -0
  82. package/dist/__tests__/dashboard-sessions.test.js +330 -0
  83. package/dist/__tests__/dashboard-sessions.test.js.map +1 -0
  84. package/dist/__tests__/dashboard-trace.test.d.ts +5 -0
  85. package/dist/__tests__/dashboard-trace.test.d.ts.map +1 -0
  86. package/dist/__tests__/dashboard-trace.test.js +147 -0
  87. package/dist/__tests__/dashboard-trace.test.js.map +1 -0
  88. package/dist/__tests__/dashboard-validation.integration.test.d.ts +14 -0
  89. package/dist/__tests__/dashboard-validation.integration.test.d.ts.map +1 -0
  90. package/dist/__tests__/dashboard-validation.integration.test.js +221 -0
  91. package/dist/__tests__/dashboard-validation.integration.test.js.map +1 -0
  92. package/dist/__tests__/dashboard-view-conformance.test.d.ts +12 -0
  93. package/dist/__tests__/dashboard-view-conformance.test.d.ts.map +1 -0
  94. package/dist/__tests__/dashboard-view-conformance.test.js +58 -0
  95. package/dist/__tests__/dashboard-view-conformance.test.js.map +1 -0
  96. package/dist/__tests__/dashboard-view-coupling.test.d.ts +7 -0
  97. package/dist/__tests__/dashboard-view-coupling.test.d.ts.map +1 -0
  98. package/dist/__tests__/dashboard-view-coupling.test.js +381 -0
  99. package/dist/__tests__/dashboard-view-coupling.test.js.map +1 -0
  100. package/dist/__tests__/dashboard-view-distribution.test.d.ts +10 -0
  101. package/dist/__tests__/dashboard-view-distribution.test.d.ts.map +1 -0
  102. package/dist/__tests__/dashboard-view-distribution.test.js +272 -0
  103. package/dist/__tests__/dashboard-view-distribution.test.js.map +1 -0
  104. package/dist/__tests__/dashboard-view-graph.test.d.ts +13 -0
  105. package/dist/__tests__/dashboard-view-graph.test.d.ts.map +1 -0
  106. package/dist/__tests__/dashboard-view-graph.test.js +332 -0
  107. package/dist/__tests__/dashboard-view-graph.test.js.map +1 -0
  108. package/dist/__tests__/dashboard-view-template.test.d.ts +21 -0
  109. package/dist/__tests__/dashboard-view-template.test.d.ts.map +1 -0
  110. package/dist/__tests__/dashboard-view-template.test.js +153 -0
  111. package/dist/__tests__/dashboard-view-template.test.js.map +1 -0
  112. package/dist/__tests__/dashboard.test.d.ts +2 -0
  113. package/dist/__tests__/dashboard.test.d.ts.map +1 -0
  114. package/dist/__tests__/dashboard.test.js +129 -0
  115. package/dist/__tests__/dashboard.test.js.map +1 -0
  116. package/dist/__tests__/graph-tab.test.d.ts +10 -0
  117. package/dist/__tests__/graph-tab.test.d.ts.map +1 -0
  118. package/dist/__tests__/graph-tab.test.js +76 -0
  119. package/dist/__tests__/graph-tab.test.js.map +1 -0
  120. package/dist/checks.d.ts +7 -0
  121. package/dist/checks.d.ts.map +1 -0
  122. package/dist/checks.js +283 -0
  123. package/dist/checks.js.map +1 -0
  124. package/dist/code-paths/__tests__/views-registry.test.d.ts +13 -0
  125. package/dist/code-paths/__tests__/views-registry.test.d.ts.map +1 -0
  126. package/dist/code-paths/__tests__/views-registry.test.js +39 -0
  127. package/dist/code-paths/__tests__/views-registry.test.js.map +1 -0
  128. package/dist/code-paths/catalog-provenance.d.ts +22 -0
  129. package/dist/code-paths/catalog-provenance.d.ts.map +1 -0
  130. package/dist/code-paths/catalog-provenance.js +108 -0
  131. package/dist/code-paths/catalog-provenance.js.map +1 -0
  132. package/dist/code-paths/catalog-recipes-tables.d.ts +11 -0
  133. package/dist/code-paths/catalog-recipes-tables.d.ts.map +1 -0
  134. package/dist/code-paths/catalog-recipes-tables.js +86 -0
  135. package/dist/code-paths/catalog-recipes-tables.js.map +1 -0
  136. package/dist/code-paths/cytoscape-vendor.d.ts +27 -0
  137. package/dist/code-paths/cytoscape-vendor.d.ts.map +1 -0
  138. package/dist/code-paths/cytoscape-vendor.js +68 -0
  139. package/dist/code-paths/cytoscape-vendor.js.map +1 -0
  140. package/dist/code-paths/editor-link.d.ts +10 -0
  141. package/dist/code-paths/editor-link.d.ts.map +1 -0
  142. package/dist/code-paths/editor-link.js +20 -0
  143. package/dist/code-paths/editor-link.js.map +1 -0
  144. package/dist/code-paths/filters.d.ts +19 -0
  145. package/dist/code-paths/filters.d.ts.map +1 -0
  146. package/dist/code-paths/filters.js +47 -0
  147. package/dist/code-paths/filters.js.map +1 -0
  148. package/dist/code-paths/function-card.d.ts +15 -0
  149. package/dist/code-paths/function-card.d.ts.map +1 -0
  150. package/dist/code-paths/function-card.js +169 -0
  151. package/dist/code-paths/function-card.js.map +1 -0
  152. package/dist/code-paths/function-row.d.ts +17 -0
  153. package/dist/code-paths/function-row.d.ts.map +1 -0
  154. package/dist/code-paths/function-row.js +77 -0
  155. package/dist/code-paths/function-row.js.map +1 -0
  156. package/dist/code-paths/graph-controls.d.ts +27 -0
  157. package/dist/code-paths/graph-controls.d.ts.map +1 -0
  158. package/dist/code-paths/graph-controls.js +257 -0
  159. package/dist/code-paths/graph-controls.js.map +1 -0
  160. package/dist/code-paths/graph-scc.d.ts +32 -0
  161. package/dist/code-paths/graph-scc.d.ts.map +1 -0
  162. package/dist/code-paths/graph-scc.js +136 -0
  163. package/dist/code-paths/graph-scc.js.map +1 -0
  164. package/dist/code-paths/graph-stylesheet.d.ts +22 -0
  165. package/dist/code-paths/graph-stylesheet.d.ts.map +1 -0
  166. package/dist/code-paths/graph-stylesheet.js +121 -0
  167. package/dist/code-paths/graph-stylesheet.js.map +1 -0
  168. package/dist/code-paths/graph-view-model.d.ts +120 -0
  169. package/dist/code-paths/graph-view-model.d.ts.map +1 -0
  170. package/dist/code-paths/graph-view-model.js +199 -0
  171. package/dist/code-paths/graph-view-model.js.map +1 -0
  172. package/dist/code-paths/help-drawer.d.ts +18 -0
  173. package/dist/code-paths/help-drawer.d.ts.map +1 -0
  174. package/dist/code-paths/help-drawer.js +54 -0
  175. package/dist/code-paths/help-drawer.js.map +1 -0
  176. package/dist/code-paths/indexes.d.ts +28 -0
  177. package/dist/code-paths/indexes.d.ts.map +1 -0
  178. package/dist/code-paths/indexes.js +97 -0
  179. package/dist/code-paths/indexes.js.map +1 -0
  180. package/dist/code-paths/path-utils.d.ts +15 -0
  181. package/dist/code-paths/path-utils.d.ts.map +1 -0
  182. package/dist/code-paths/path-utils.js +47 -0
  183. package/dist/code-paths/path-utils.js.map +1 -0
  184. package/dist/code-paths/search.d.ts +14 -0
  185. package/dist/code-paths/search.d.ts.map +1 -0
  186. package/dist/code-paths/search.js +54 -0
  187. package/dist/code-paths/search.js.map +1 -0
  188. package/dist/code-paths/trace.d.ts +11 -0
  189. package/dist/code-paths/trace.d.ts.map +1 -0
  190. package/dist/code-paths/trace.js +60 -0
  191. package/dist/code-paths/trace.js.map +1 -0
  192. package/dist/code-paths/view-coupling.d.ts +22 -0
  193. package/dist/code-paths/view-coupling.d.ts.map +1 -0
  194. package/dist/code-paths/view-coupling.js +218 -0
  195. package/dist/code-paths/view-coupling.js.map +1 -0
  196. package/dist/code-paths/view-distribution.d.ts +20 -0
  197. package/dist/code-paths/view-distribution.d.ts.map +1 -0
  198. package/dist/code-paths/view-distribution.js +82 -0
  199. package/dist/code-paths/view-distribution.js.map +1 -0
  200. package/dist/code-paths/view-graph.d.ts +35 -0
  201. package/dist/code-paths/view-graph.d.ts.map +1 -0
  202. package/dist/code-paths/view-graph.js +379 -0
  203. package/dist/code-paths/view-graph.js.map +1 -0
  204. package/dist/code-paths/view-template.d.ts +154 -0
  205. package/dist/code-paths/view-template.d.ts.map +1 -0
  206. package/dist/code-paths/view-template.js +218 -0
  207. package/dist/code-paths/view-template.js.map +1 -0
  208. package/dist/code-paths/views-registry.d.ts +13 -0
  209. package/dist/code-paths/views-registry.d.ts.map +1 -0
  210. package/dist/code-paths/views-registry.js +53 -0
  211. package/dist/code-paths/views-registry.js.map +1 -0
  212. package/dist/code-paths.d.ts +69 -0
  213. package/dist/code-paths.d.ts.map +1 -0
  214. package/dist/code-paths.js +356 -0
  215. package/dist/code-paths.js.map +1 -0
  216. package/dist/css/cards.d.ts +9 -0
  217. package/dist/css/cards.d.ts.map +1 -0
  218. package/dist/css/cards.js +36 -0
  219. package/dist/css/cards.js.map +1 -0
  220. package/dist/css/code-paths.d.ts +9 -0
  221. package/dist/css/code-paths.d.ts.map +1 -0
  222. package/dist/css/code-paths.js +111 -0
  223. package/dist/css/code-paths.js.map +1 -0
  224. package/dist/css/data-table.d.ts +12 -0
  225. package/dist/css/data-table.d.ts.map +1 -0
  226. package/dist/css/data-table.js +103 -0
  227. package/dist/css/data-table.js.map +1 -0
  228. package/dist/css/function-card.d.ts +6 -0
  229. package/dist/css/function-card.d.ts.map +1 -0
  230. package/dist/css/function-card.js +26 -0
  231. package/dist/css/function-card.js.map +1 -0
  232. package/dist/css/help-drawer.d.ts +6 -0
  233. package/dist/css/help-drawer.d.ts.map +1 -0
  234. package/dist/css/help-drawer.js +22 -0
  235. package/dist/css/help-drawer.js.map +1 -0
  236. package/dist/css/tabs.d.ts +9 -0
  237. package/dist/css/tabs.d.ts.map +1 -0
  238. package/dist/css/tabs.js +28 -0
  239. package/dist/css/tabs.js.map +1 -0
  240. package/dist/css/theme.d.ts +8 -0
  241. package/dist/css/theme.d.ts.map +1 -0
  242. package/dist/css/theme.js +34 -0
  243. package/dist/css/theme.js.map +1 -0
  244. package/dist/css.d.ts +13 -0
  245. package/dist/css.d.ts.map +1 -0
  246. package/dist/css.js +30 -0
  247. package/dist/css.js.map +1 -0
  248. package/dist/generator.d.ts +33 -0
  249. package/dist/generator.d.ts.map +1 -0
  250. package/dist/generator.js +161 -0
  251. package/dist/generator.js.map +1 -0
  252. package/dist/index.d.ts +21 -0
  253. package/dist/index.d.ts.map +1 -0
  254. package/dist/index.js +20 -0
  255. package/dist/index.js.map +1 -0
  256. package/dist/overview.d.ts +13 -0
  257. package/dist/overview.d.ts.map +1 -0
  258. package/dist/overview.js +91 -0
  259. package/dist/overview.js.map +1 -0
  260. package/dist/recipes.d.ts +6 -0
  261. package/dist/recipes.d.ts.map +1 -0
  262. package/dist/recipes.js +68 -0
  263. package/dist/recipes.js.map +1 -0
  264. package/dist/sessions.d.ts +6 -0
  265. package/dist/sessions.d.ts.map +1 -0
  266. package/dist/sessions.js +288 -0
  267. package/dist/sessions.js.map +1 -0
  268. package/dist/shared/el.d.ts +13 -0
  269. package/dist/shared/el.d.ts.map +1 -0
  270. package/dist/shared/el.js +27 -0
  271. package/dist/shared/el.js.map +1 -0
  272. package/dist/shared/pagination.d.ts +15 -0
  273. package/dist/shared/pagination.d.ts.map +1 -0
  274. package/dist/shared/pagination.js +113 -0
  275. package/dist/shared/pagination.js.map +1 -0
  276. package/dist/shared/sortable.d.ts +14 -0
  277. package/dist/shared/sortable.d.ts.map +1 -0
  278. package/dist/shared/sortable.js +101 -0
  279. package/dist/shared/sortable.js.map +1 -0
  280. package/dist/shared/tab-activators.d.ts +16 -0
  281. package/dist/shared/tab-activators.d.ts.map +1 -0
  282. package/dist/shared/tab-activators.js +33 -0
  283. package/dist/shared/tab-activators.js.map +1 -0
  284. package/dist/shared/tab-bar.d.ts +8 -0
  285. package/dist/shared/tab-bar.d.ts.map +1 -0
  286. package/dist/shared/tab-bar.js +20 -0
  287. package/dist/shared/tab-bar.js.map +1 -0
  288. package/dist/shared.d.ts +26 -0
  289. package/dist/shared.d.ts.map +1 -0
  290. package/dist/shared.js +39 -0
  291. package/dist/shared.js.map +1 -0
  292. package/dist/subtab-bar.d.ts +23 -0
  293. package/dist/subtab-bar.d.ts.map +1 -0
  294. package/dist/subtab-bar.js +77 -0
  295. package/dist/subtab-bar.js.map +1 -0
  296. package/dist/tool-tab-registry.d.ts +76 -0
  297. package/dist/tool-tab-registry.d.ts.map +1 -0
  298. package/dist/tool-tab-registry.js +44 -0
  299. package/dist/tool-tab-registry.js.map +1 -0
  300. package/dist/tool-tabs-registrations.d.ts +19 -0
  301. package/dist/tool-tabs-registrations.d.ts.map +1 -0
  302. package/dist/tool-tabs-registrations.js +46 -0
  303. package/dist/tool-tabs-registrations.js.map +1 -0
  304. package/dist/tool-tabs.d.ts +12 -0
  305. package/dist/tool-tabs.d.ts.map +1 -0
  306. package/dist/tool-tabs.js +80 -0
  307. package/dist/tool-tabs.js.map +1 -0
  308. package/dist/vendor/cytoscape-bundle.js +600 -0
  309. package/package.json +52 -0
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Overview tab — cross-tool recent activity table.
3
+ * Returns JS code as a string.
4
+ *
5
+ * The `toolBadgeStyles` and `tabMap` literals spliced into the emitted
6
+ * JS are derived from the `defineToolTab` registry rather than
7
+ * hard-coded — every registered tool tab contributes one entry to
8
+ * each map. Adding a new tool is a `defineToolTab` call; this
9
+ * function picks it up automatically. (F1/F8.)
10
+ */
11
+ import { listToolTabs } from './tool-tab-registry.js';
12
+ import './tool-tabs-registrations.js'; // side-effect: registers fit/sim/graph
13
+ export function dashboardOverviewJs() {
14
+ const toolTabs = listToolTabs();
15
+ const badgeStylesEntries = toolTabs
16
+ .map((t) => ` ${JSON.stringify(t.tool)}: ${JSON.stringify(t.badgeStyle)},`)
17
+ .join('\n');
18
+ const tabMapEntries = toolTabs
19
+ .map((t) => `${JSON.stringify(t.tool)}: ${JSON.stringify(t.id)}`)
20
+ .join(', ');
21
+ return `
22
+ // =======================================================
23
+ // OVERVIEW TAB
24
+ // =======================================================
25
+ function renderOverview() {
26
+ const panel = document.getElementById('panel-overview');
27
+ if (!sessions.length) { panel.appendChild(el('div', {class:'empty', text:'No sessions yet.'})); return; }
28
+
29
+ const sec = el('div', {class:'section'}, [el('h3', {text:'Recent Activity'})]);
30
+ const table = el('table', {class:'data-table sortable'});
31
+ const thead = el('thead');
32
+ const headerRow = el('tr');
33
+ ['Timestamp', 'Tool', 'Recipe', 'Pass Rate', 'Status', 'Checks', 'Findings', 'Duration'].forEach(h => {
34
+ headerRow.appendChild(el('th', {text: h}));
35
+ });
36
+ thead.appendChild(headerRow);
37
+ table.appendChild(thead);
38
+
39
+ const tbody = el('tbody');
40
+ // Derived from the defineToolTab registry — see tool-tab-registry.ts
41
+ // and tool-tabs-registrations.ts. New tools register descriptors;
42
+ // these maps update automatically.
43
+ const toolBadgeStyles = {
44
+ ${badgeStylesEntries}
45
+ };
46
+ const tabMap = { ${tabMapEntries} };
47
+
48
+ sessions.forEach(s => {
49
+ const sc2 = s.score >= 90 ? 'color:var(--success)' : s.score >= 70 ? 'color:var(--warning)' : 'color:var(--error)';
50
+ // Per-session counts live in the tool-owned opaque payload. Tools
51
+ // that persist no summary (or none yet) fall back to zeros so the
52
+ // cross-tool row stays well-formed.
53
+ const sm = (s.payload && s.payload.summary) || { total: 0, passed: 0, failed: 0, errors: 0, warnings: 0 };
54
+ const row = el('tr', {class:'clickable', onclick: () => {
55
+ // Tabs that need session-aware deep-linking (Code Paths today;
56
+ // future fit/sim detail views) register an activator into the
57
+ // shared tabActivators registry. If one matches this session's
58
+ // tool, hand off to it. Otherwise fall back to plain top-level
59
+ // tab switching by name.
60
+ if (activateTabForSession(s)) return;
61
+ const tabName = tabMap[s.tool] || s.tool;
62
+ document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
63
+ document.querySelectorAll('.tab-panel').forEach(p => p.classList.remove('active'));
64
+ const tab = document.querySelector('.tab[data-tab="' + tabName + '"]');
65
+ if (tab) tab.classList.add('active');
66
+ const panel = document.getElementById('panel-' + tabName);
67
+ if (panel) panel.classList.add('active');
68
+ }});
69
+ row.appendChild(el('td', {class:'cell-nowrap', text: new Date(s.startedAt).toLocaleString(), style:'color:var(--text-dim)'}));
70
+ const toolCell = el('td');
71
+ toolCell.appendChild(el('span', {class:'badge', style: toolBadgeStyles[s.tool] || '', text: s.tool.toUpperCase()}));
72
+ row.appendChild(toolCell);
73
+ row.appendChild(el('td', {text: s.recipe || 'default', style:'color:var(--text-muted)'}));
74
+ row.appendChild(el('td', {text: s.score+'%', style:'font-weight:600;'+sc2}));
75
+ const statusCell = el('td');
76
+ statusCell.appendChild(statusBadge(sessionStatus(s)));
77
+ row.appendChild(statusCell);
78
+ row.appendChild(el('td', {text: sm.passed+'/'+sm.total}));
79
+ row.appendChild(el('td', {text: ''+(sm.errors + (sm.warnings || 0))}));
80
+ row.appendChild(el('td', {text: (s.durationMs/1000).toFixed(1)+'s', style:'color:var(--text-dim)'}));
81
+ tbody.appendChild(row);
82
+ });
83
+ table.appendChild(tbody);
84
+ const pag = el('div', {class:'pagination'});
85
+ sec.appendChild(el('div', {class:'card'}, [table, pag]));
86
+ panel.appendChild(sec);
87
+ paginateTable(tbody, pag, 10);
88
+ }
89
+ `;
90
+ }
91
+ //# sourceMappingURL=overview.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"overview.js","sourceRoot":"","sources":["../src/overview.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,8BAA8B,CAAC,CAAC,uCAAuC;AAE9E,MAAM,UAAU,mBAAmB;IACjC,MAAM,QAAQ,GAAG,YAAY,EAAE,CAAC;IAChC,MAAM,kBAAkB,GAAG,QAAQ;SAChC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC;SAC7E,IAAI,CAAC,IAAI,CAAC,CAAC;IACd,MAAM,aAAa,GAAG,QAAQ;SAC3B,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;SAChE,IAAI,CAAC,IAAI,CAAC,CAAC;IACd,OAAO;;;;;;;;;;;;;;;;;;;;;;;EAuBP,kBAAkB;;qBAEC,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2CjC,CAAC;AACF,CAAC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Recipes catalog rendering — shows available recipes with their configuration.
3
+ * Returns JS code as a string.
4
+ */
5
+ export declare function dashboardRecipesJs(): string;
6
+ //# sourceMappingURL=recipes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recipes.d.ts","sourceRoot":"","sources":["../src/recipes.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,wBAAgB,kBAAkB,IAAI,MAAM,CA8D3C"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Recipes catalog rendering — shows available recipes with their configuration.
3
+ * Returns JS code as a string.
4
+ */
5
+ export function dashboardRecipesJs() {
6
+ return `
7
+ // =======================================================
8
+ // RECIPES CATALOG
9
+ // =======================================================
10
+
11
+ function renderRecipesPanel(container, recipesData) {
12
+ if (!recipesData || !recipesData.length) {
13
+ container.appendChild(el('div', {class:'empty', text:'No recipes available.'}));
14
+ return;
15
+ }
16
+
17
+ const table = el('table', {class:'data-table'});
18
+ const thead = el('thead');
19
+ const headerRow = el('tr');
20
+ ['Recipe', 'Description', 'Selector', 'Mode', 'Timeout', 'Tags'].forEach(h => {
21
+ headerRow.appendChild(el('th', {text: h}));
22
+ });
23
+ thead.appendChild(headerRow);
24
+ table.appendChild(thead);
25
+
26
+ const tbody = el('tbody');
27
+ recipesData.forEach(recipe => {
28
+ const row = el('tr');
29
+
30
+ // Name
31
+ const nameCell = el('td', {style:'font-weight:500'});
32
+ nameCell.appendChild(el('div', {text: recipe.displayName}));
33
+ nameCell.appendChild(el('div', {text: recipe.name, style:'font-size:11px;color:var(--text-dim);font-weight:400'}));
34
+ row.appendChild(nameCell);
35
+
36
+ // Description
37
+ row.appendChild(el('td', {text: recipe.description, style:'color:var(--text-muted)'}));
38
+
39
+ // Selector type
40
+ const selCell = el('td');
41
+ selCell.appendChild(el('span', {class:'badge', style:'background:var(--bg-hover);color:var(--text-muted)', text: recipe.selectorType}));
42
+ row.appendChild(selCell);
43
+
44
+ // Mode
45
+ const modeCell = el('td');
46
+ const modeColor = recipe.mode === 'parallel' ? 'color:var(--success)' : 'color:var(--warning)';
47
+ modeCell.appendChild(el('span', {text: recipe.mode, style: modeColor + ';font-size:12px'}));
48
+ row.appendChild(modeCell);
49
+
50
+ // Timeout
51
+ row.appendChild(el('td', {text: (recipe.timeout / 1000) + 's', style:'color:var(--text-dim);font-size:12px'}));
52
+
53
+ // Tags
54
+ const tagsCell = el('td');
55
+ (recipe.tags || []).forEach(t => {
56
+ tagsCell.appendChild(el('span', {class:'tag-badge', text: t}));
57
+ });
58
+ row.appendChild(tagsCell);
59
+
60
+ tbody.appendChild(row);
61
+ });
62
+
63
+ table.appendChild(tbody);
64
+ container.appendChild(el('div', {class:'card'}, [table]));
65
+ }
66
+ `;
67
+ }
68
+ //# sourceMappingURL=recipes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recipes.js","sourceRoot":"","sources":["../src/recipes.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,UAAU,kBAAkB;IAChC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4DR,CAAC;AACF,CAAC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Session table + session detail rendering — used by fitness/sim tabs.
3
+ * Returns JS code as a string.
4
+ */
5
+ export declare function dashboardSessionsJs(): string;
6
+ //# sourceMappingURL=sessions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessions.d.ts","sourceRoot":"","sources":["../src/sessions.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,wBAAgB,mBAAmB,IAAI,MAAM,CA0R5C"}
@@ -0,0 +1,288 @@
1
+ /**
2
+ * Session table + session detail rendering — used by fitness/sim tabs.
3
+ * Returns JS code as a string.
4
+ */
5
+ export function dashboardSessionsJs() {
6
+ return String.raw `
7
+ // =======================================================
8
+ // SESSION TABLE (used by fitness/sim tabs)
9
+ // =======================================================
10
+
11
+ // Per-rule metric column map for the expanded findings table. For these
12
+ // graph rules the finding message just repeats the file + the metric, so
13
+ // we DROP the Message column and render a dedicated metric column read
14
+ // from finding.metadata (persisted on the signal metadata payload).
15
+ const RULE_METRIC_COLUMNS = {
16
+ 'graph:large-function': { label: 'Lines', key: 'bodyLines' },
17
+ 'graph:high-blast-untested': { label: 'Score', key: 'blast' },
18
+ 'graph:wide-function': { label: 'Parameters', key: 'paramCount' },
19
+ 'graph:cycle': { label: 'Call Cycle', key: 'sccSize' },
20
+ };
21
+
22
+ /** Derive 3-state session status: 'fail' | 'warn' | 'pass'
23
+ * Counts live in the tool-owned opaque payload (summary). */
24
+ function sessionStatus(s) {
25
+ const sm = (s.payload && s.payload.summary) || {};
26
+ if (sm.failed > 0) return 'fail';
27
+ if (sm.warnings > 0) return 'warn';
28
+ return 'pass';
29
+ }
30
+
31
+ function statusBadge(status) {
32
+ const labels = { fail: 'FAIL', warn: 'WARN', pass: 'PASS' };
33
+ const classes = { fail: 'badge-fail', warn: 'badge-warn', pass: 'badge-pass' };
34
+ return el('span', {class:'badge ' + classes[status], text: labels[status]});
35
+ }
36
+
37
+ function renderSessionTable(panel, toolSessions, accentColor) {
38
+ if (!toolSessions.length) {
39
+ panel.appendChild(el('div', {class:'empty', text:'No sessions yet.'}));
40
+ return;
41
+ }
42
+
43
+ const tool = toolSessions[0].tool;
44
+
45
+ const table = el('table', {class:'data-table sortable'});
46
+ const thead = el('thead');
47
+ const headerRow = el('tr');
48
+ ['Timestamp', 'Recipe', 'Pass Rate', 'Status', 'Passed', 'Failed', 'Findings', 'Duration'].forEach(h => {
49
+ headerRow.appendChild(el('th', {text: h}));
50
+ });
51
+ thead.appendChild(headerRow);
52
+ table.appendChild(thead);
53
+
54
+ const tbody = el('tbody');
55
+ toolSessions.forEach((s, idx) => {
56
+ const sc = s.score >= 90 ? 'color:var(--success)' : s.score >= 70 ? 'color:var(--warning)' : 'color:var(--error)';
57
+ const sm = (s.payload && s.payload.summary) || { total: 0, passed: 0, failed: 0, errors: 0, warnings: 0 };
58
+ const row = el('tr', {class:'clickable', id: 'session-row-' + tool + '-' + idx, 'data-session-id': s.id, onclick: () => {
59
+ tbody.querySelectorAll('tr.selected').forEach(r => r.classList.remove('selected'));
60
+ row.classList.add('selected');
61
+ renderDetail(s, idx);
62
+ }});
63
+ row.appendChild(el('td', {class:'cell-nowrap', text: new Date(s.startedAt).toLocaleString()}));
64
+ row.appendChild(el('td', {text: s.recipe || 'default', style:'color:var(--text-muted)'}));
65
+ const scoreCell = el('td', {style: 'font-weight:600;' + sc});
66
+ scoreCell.textContent = s.score + '%';
67
+ row.appendChild(scoreCell);
68
+ const badgeCell = el('td');
69
+ badgeCell.appendChild(statusBadge(sessionStatus(s)));
70
+ row.appendChild(badgeCell);
71
+ row.appendChild(el('td', {text: ''+sm.passed, style:'color:var(--success)'}));
72
+ row.appendChild(el('td', {text: ''+sm.failed, style: sm.failed > 0 ? 'color:var(--error)' : 'color:var(--text-dim)'}));
73
+ row.appendChild(el('td', {text: ''+(sm.errors + (sm.warnings || 0))}));
74
+ row.appendChild(el('td', {text: (s.durationMs/1000).toFixed(1)+'s', style:'color:var(--text-dim)'}));
75
+ tbody.appendChild(row);
76
+ });
77
+ table.appendChild(tbody);
78
+
79
+ const sessionPag = el('div', {class:'pagination'});
80
+ const sec = el('div', {class:'section'}, [el('h3', {text:'Sessions (' + toolSessions.length + ')'}), el('div', {class:'card'}, [table, sessionPag])]);
81
+ panel.appendChild(sec);
82
+ paginateTable(tbody, sessionPag, 10);
83
+
84
+ // Detail container — kept as a direct reference, no global ID lookup needed
85
+ const detailContainer = el('div', {id: 'detail-' + tool + '-' + Math.random().toString(36).slice(2,8), class:'section', style:'display:none'});
86
+ panel.appendChild(detailContainer);
87
+
88
+ function renderDetail(session, idx) {
89
+ detailContainer.style.display = 'block';
90
+ while (detailContainer.firstChild) detailContainer.removeChild(detailContainer.firstChild);
91
+
92
+ // Sessions written before the tool-owned payload split (or by a tool
93
+ // that records no per-item detail) have no payload at all. Distinguish
94
+ // that from "payload present but empty" so the panel says so explicitly
95
+ // rather than rendering a silent empty table.
96
+ if (!session.payload) {
97
+ detailContainer.appendChild(el('h3', {text: 'Session Detail — ' + new Date(session.startedAt).toLocaleString(), style:'margin-bottom:4px'}));
98
+ detailContainer.appendChild(el('div', {class:'empty', text:'No detail recorded for this session.'}));
99
+ return;
100
+ }
101
+
102
+ // Per-item detail lives in the tool-owned opaque payload. Fitness calls
103
+ // these "checks"; graph groups signals by rule (relabeled below).
104
+ const checks = (session.payload && session.payload.checks) || [];
105
+
106
+ // A payload that records no per-item rows (e.g. a clean graph run — graph
107
+ // only persists rules that emitted a finding) would otherwise render a
108
+ // header-only table that reads as "nothing shows up". Render an explicit
109
+ // empty state instead so a clean run is unambiguous.
110
+ if (checks.length === 0) {
111
+ detailContainer.appendChild(el('h3', {text: 'Session Detail — ' + new Date(session.startedAt).toLocaleString(), style:'margin-bottom:4px'}));
112
+ const sm = (session.payload && session.payload.summary) || {};
113
+ const clean = !((sm.errors || 0) > 0 || (sm.warnings || 0) > 0);
114
+ const subline = el('div', {style:'color:var(--text-dim);font-size:12px;margin-bottom:12px'});
115
+ subline.textContent = session.cwd + (session.recipe ? ' — recipe: ' + session.recipe : '');
116
+ detailContainer.appendChild(subline);
117
+ detailContainer.appendChild(el('div', {class:'empty', text: clean
118
+ ? 'No findings — this run was clean. Every rule passed with zero violations.'
119
+ : 'No per-rule detail was recorded for this run.'}));
120
+ return;
121
+ }
122
+
123
+ // Compute session-level totals from check findings
124
+ let totalErrors = 0;
125
+ let totalWarnings = 0;
126
+ checks.forEach(c => {
127
+ if (c.findings) {
128
+ c.findings.forEach(f => {
129
+ if (f.severity === 'error') totalErrors++;
130
+ else if (f.severity === 'warning') totalWarnings++;
131
+ });
132
+ }
133
+ });
134
+
135
+ const headerRow = el('div', {style:'display:flex;align-items:center;justify-content:space-between;margin-bottom:16px'});
136
+ const headerLeft = el('div');
137
+ headerLeft.appendChild(el('h3', {text: 'Session Detail \u2014 ' + new Date(session.startedAt).toLocaleString(), style:'margin-bottom:4px'}));
138
+ const sub = el('div', {style:'color:var(--text-dim);font-size:12px'});
139
+ const countParts = [];
140
+ if (totalErrors > 0) countParts.push(totalErrors + ' error' + (totalErrors !== 1 ? 's' : ''));
141
+ if (totalWarnings > 0) countParts.push(totalWarnings + ' warning' + (totalWarnings !== 1 ? 's' : ''));
142
+ const countsStr = countParts.length > 0 ? ' \u2014 ' + countParts.join(', ') : '';
143
+ sub.textContent = session.cwd + (session.recipe ? ' \u2014 recipe: ' + session.recipe : '') + countsStr;
144
+ headerLeft.appendChild(sub);
145
+ headerRow.appendChild(headerLeft);
146
+
147
+ detailContainer.appendChild(headerRow);
148
+ const filterUid = 'df-' + tool + '-' + idx + '-' + Math.random().toString(36).slice(2,6);
149
+
150
+ // Check detail table
151
+ const table = el('table', {class:'data-table sortable'});
152
+ const thead = el('thead');
153
+ const thRow = el('tr');
154
+ // Graph groups findings by rule, not by check — relabel that one
155
+ // column so the header reads in the tool's own vocabulary. The
156
+ // structural payload shape is identical; only the label differs.
157
+ const itemColumn = tool === 'graph' ? 'Rule' : 'Check';
158
+ // Graph rules are dataset queries, not timed units — their per-rule
159
+ // duration is always 0ms, so drop the Duration column for graph
160
+ // sessions. Fitness/sim checks ARE timed; keep it for them.
161
+ const showDuration = tool !== 'graph';
162
+ const itemHeaders = ['', itemColumn, 'Status', 'Errors', 'Warnings', 'Findings'];
163
+ if (showDuration) itemHeaders.push('Duration');
164
+ itemHeaders.forEach(h => {
165
+ thRow.appendChild(el('th', {text: h}));
166
+ });
167
+ thead.appendChild(thRow);
168
+ table.appendChild(thead);
169
+
170
+ const tbody = el('tbody');
171
+ // Sort rules/checks by severity weight: most errors first, then by
172
+ // warning count as a tiebreak (error-then-warning, stable).
173
+ const sortedChecks = [...checks].sort((a, b) => {
174
+ const aErrors = a.findings ? a.findings.filter(f => f.severity === 'error').length : 0;
175
+ const bErrors = b.findings ? b.findings.filter(f => f.severity === 'error').length : 0;
176
+ if (bErrors !== aErrors) return bErrors - aErrors;
177
+ const aWarn = a.findings ? a.findings.filter(f => f.severity === 'warning').length : 0;
178
+ const bWarn = b.findings ? b.findings.filter(f => f.severity === 'warning').length : 0;
179
+ return bWarn - aWarn;
180
+ });
181
+ sortedChecks.forEach((check, i) => {
182
+ const checkErrors = check.findings ? check.findings.filter(f => f.severity === 'error').length : 0;
183
+ const checkWarnings = check.findings ? check.findings.filter(f => f.severity === 'warning').length : 0;
184
+ const findingsTotal = checkErrors + checkWarnings;
185
+ const hasFindings = findingsTotal > 0;
186
+ const expanderId = filterUid + '-exp-' + i;
187
+ const checkStatusVal = check.passed ? 'pass' : 'fail';
188
+
189
+ const arrowCell = el('td', {style:'width:24px;text-align:center;color:var(--text-dim);font-size:12px'});
190
+ if (hasFindings) arrowCell.textContent = '\u25B6';
191
+
192
+ const row = el('tr', {class: hasFindings ? 'clickable' : '', 'data-check-status': checkStatusVal, onclick: hasFindings ? () => {
193
+ const exp = document.getElementById(expanderId);
194
+ if (exp) {
195
+ const isOpen = exp.classList.toggle('open');
196
+ exp.style.display = isOpen ? 'table-row' : 'none';
197
+ arrowCell.textContent = isOpen ? '\u25BC' : '\u25B6';
198
+ }
199
+ row.classList.toggle('expanded');
200
+ } : undefined});
201
+ row.appendChild(arrowCell);
202
+ row.appendChild(el('td', {text: check.checkSlug, style:'font-weight:500'}));
203
+
204
+ const statusCell = el('td');
205
+ statusCell.appendChild(el('span', {class:'badge ' + (check.passed ? 'badge-pass' : 'badge-fail'), text: check.passed ? 'PASS' : 'FAIL'}));
206
+ row.appendChild(statusCell);
207
+ row.appendChild(el('td', {text: ''+checkErrors, style: checkErrors > 0 ? 'color:var(--error)' : 'color:var(--text-dim)'}));
208
+ row.appendChild(el('td', {text: ''+checkWarnings, style: checkWarnings > 0 ? 'color:var(--warning)' : 'color:var(--text-dim)'}));
209
+ row.appendChild(el('td', {text: ''+findingsTotal, style: findingsTotal > 0 ? 'color:var(--text)' : 'color:var(--text-dim)'}));
210
+ if (showDuration) row.appendChild(el('td', {text: check.durationMs > 0 ? check.durationMs + 'ms' : '0ms', style:'color:var(--text-dim)'}));
211
+ tbody.appendChild(row);
212
+
213
+ if (hasFindings) {
214
+ const expRow = el('tr', {id: expanderId, class:'expander-row', 'data-check-status': checkStatusVal});
215
+ const expCell = el('td', {colspan: '' + itemHeaders.length, style:'padding:0'});
216
+ const expContent = el('div', {class:'expander-content'});
217
+
218
+ const fTable = el('table', {class:'data-table', style:'margin:0;border:none'});
219
+ const fHead = el('thead');
220
+ const fHeaderRow = el('tr');
221
+ // Per-rule column shape. Most rules render the default
222
+ // [Severity, Message, File, Suggestion]. The graph metric rules
223
+ // below repeat the file + metric in their message, so they DROP
224
+ // Message and ADD a metric column read from finding.metadata.
225
+ const metricColumn = RULE_METRIC_COLUMNS[check.checkSlug];
226
+ const fHeaders = metricColumn
227
+ ? ['Severity', 'File', metricColumn.label, 'Suggestion']
228
+ : ['Severity', 'Message', 'File', 'Suggestion'];
229
+ fHeaders.forEach(h => {
230
+ fHeaderRow.appendChild(el('th', {text: h, style:'font-size:11px;padding:6px 12px'}));
231
+ });
232
+ fHead.appendChild(fHeaderRow);
233
+ fTable.appendChild(fHead);
234
+
235
+ const fBody = el('tbody');
236
+ // Sort findings within the rule: errors first, then warnings (stable).
237
+ const sevWeight = { error: 0, warning: 1 };
238
+ const sortedFindings = [...check.findings].sort((a, b) =>
239
+ (sevWeight[a.severity] ?? 2) - (sevWeight[b.severity] ?? 2));
240
+ sortedFindings.forEach(f => {
241
+ const fRow = el('tr');
242
+ const sevCell = el('td', {style:'padding:6px 12px'});
243
+ sevCell.appendChild(el('span', {class:'finding-sev ' + f.severity, text: f.severity}));
244
+ fRow.appendChild(sevCell);
245
+ const fileText = f.filePath ? f.filePath + (f.line ? ':' + f.line : '') : '\u2014';
246
+ if (metricColumn) {
247
+ fRow.appendChild(el('td', {text: fileText, style:'padding:6px 12px;color:var(--text-dim);font-size:12px'}));
248
+ const mv = f.metadata ? f.metadata[metricColumn.key] : undefined;
249
+ fRow.appendChild(el('td', {text: (mv === undefined || mv === null) ? '\u2014' : '' + mv, style:'padding:6px 12px;font-size:13px'}));
250
+ } else {
251
+ fRow.appendChild(el('td', {text: f.message, style:'padding:6px 12px;font-size:13px'}));
252
+ fRow.appendChild(el('td', {text: fileText, style:'padding:6px 12px;color:var(--text-dim);font-size:12px'}));
253
+ }
254
+ fRow.appendChild(el('td', {text: f.suggestion || '\u2014', style:'padding:6px 12px;color:var(--accent);font-size:12px'}));
255
+ fBody.appendChild(fRow);
256
+ });
257
+ fTable.appendChild(fBody);
258
+ // Wrap the wide findings table in a horizontal-scroll container so
259
+ // long file paths / messages scroll inside the card instead of
260
+ // overrunning the section (mirrors the .coupling-scroll fix).
261
+ const fScroll = el('div', {style:'overflow-x:auto;max-width:100%'}, [fTable]);
262
+ expContent.appendChild(fScroll);
263
+ expCell.appendChild(expContent);
264
+ expRow.appendChild(expCell);
265
+ tbody.appendChild(expRow);
266
+ }
267
+ });
268
+ table.appendChild(tbody);
269
+ const detailPag = el('div', {class:'pagination'});
270
+ detailContainer.appendChild(el('div', {class:'card'}, [table, detailPag]));
271
+
272
+ // Enable sorting on the detail table
273
+ makeSortable(table);
274
+
275
+ // Paginate
276
+ paginateGroupedRows(tbody, detailPag, 10);
277
+ }
278
+
279
+ // Auto-show latest and highlight first row
280
+ renderDetail(toolSessions[0], 0);
281
+ const firstRow = tbody.querySelector('tr');
282
+ if (firstRow) firstRow.classList.add('selected');
283
+ }
284
+
285
+
286
+ `;
287
+ }
288
+ //# sourceMappingURL=sessions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessions.js","sourceRoot":"","sources":["../src/sessions.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,UAAU,mBAAmB;IACjC,OAAO,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwRlB,CAAC;AACF,CAAC"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * `el(tag, attrs, children)` — tiny DOM-builder helper used everywhere
3
+ * a string of `document.createElement` + setAttribute calls would
4
+ * otherwise live.
5
+ *
6
+ * Special attribute keys: `text` sets `textContent`, `class` sets
7
+ * `className`, anything starting with `on` is treated as an event
8
+ * listener (e.g. `onclick`), everything else passes through to
9
+ * `setAttribute`. `children` may include strings (auto-wrapped into
10
+ * text nodes) or `null`/`undefined` (skipped).
11
+ */
12
+ export declare function dashboardElJs(): string;
13
+ //# sourceMappingURL=el.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"el.d.ts","sourceRoot":"","sources":["../../src/shared/el.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,IAAI,MAAM,CActC"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * `el(tag, attrs, children)` — tiny DOM-builder helper used everywhere
3
+ * a string of `document.createElement` + setAttribute calls would
4
+ * otherwise live.
5
+ *
6
+ * Special attribute keys: `text` sets `textContent`, `class` sets
7
+ * `className`, anything starting with `on` is treated as an event
8
+ * listener (e.g. `onclick`), everything else passes through to
9
+ * `setAttribute`. `children` may include strings (auto-wrapped into
10
+ * text nodes) or `null`/`undefined` (skipped).
11
+ */
12
+ export function dashboardElJs() {
13
+ return String.raw `
14
+ function el(tag, attrs, children) {
15
+ const e = document.createElement(tag);
16
+ if (attrs) Object.entries(attrs).forEach(([k,v]) => {
17
+ if (k === 'text') e.textContent = v;
18
+ else if (k === 'class') e.className = v;
19
+ else if (k.startsWith('on')) e.addEventListener(k.slice(2), v);
20
+ else e.setAttribute(k, v);
21
+ });
22
+ if (children) children.forEach(c => { if (typeof c === 'string') e.appendChild(document.createTextNode(c)); else if (c) e.appendChild(c); });
23
+ return e;
24
+ }
25
+ `;
26
+ }
27
+ //# sourceMappingURL=el.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"el.js","sourceRoot":"","sources":["../../src/shared/el.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,MAAM,UAAU,aAAa;IAC3B,OAAO,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;CAYlB,CAAC;AACF,CAAC"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Pagination helpers — `paginateTable`, `paginateGroupedRows`, and the
3
+ * page-button renderer.
4
+ *
5
+ * Every data-table in the dashboard paginates at 10 rows/page (or
6
+ * 10 groups/page when expander rows are present). The grouped variant
7
+ * keeps a data row and its trailing `.expander-row` together so they
8
+ * page as one unit.
9
+ *
10
+ * `renderPageButtons` is shared between both paginators; the checks
11
+ * catalog declares its own paginator inline that also calls this
12
+ * helper, so it must be in scope before any caller.
13
+ */
14
+ export declare function dashboardPaginationJs(): string;
15
+ //# sourceMappingURL=pagination.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pagination.d.ts","sourceRoot":"","sources":["../../src/shared/pagination.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,CAkG9C"}