@flanksource/clicky-ui 0.3.47 → 0.3.49

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 (255) hide show
  1. package/README.md +49 -0
  2. package/dist/comments/CommentSidePanel.cjs +10 -4
  3. package/dist/comments/CommentSidePanel.cjs.map +1 -1
  4. package/dist/comments/CommentSidePanel.d.ts +2 -0
  5. package/dist/comments/CommentSidePanel.d.ts.map +1 -1
  6. package/dist/comments/CommentSidePanel.js +10 -4
  7. package/dist/comments/CommentSidePanel.js.map +1 -1
  8. package/dist/comments/CommentThread.cjs +2 -0
  9. package/dist/comments/CommentThread.cjs.map +1 -1
  10. package/dist/comments/CommentThread.d.ts +2 -1
  11. package/dist/comments/CommentThread.d.ts.map +1 -1
  12. package/dist/comments/CommentThread.js +2 -0
  13. package/dist/comments/CommentThread.js.map +1 -1
  14. package/dist/comments/CommentThreadList.cjs +3 -0
  15. package/dist/comments/CommentThreadList.cjs.map +1 -1
  16. package/dist/comments/CommentThreadList.d.ts +3 -1
  17. package/dist/comments/CommentThreadList.d.ts.map +1 -1
  18. package/dist/comments/CommentThreadList.js +3 -0
  19. package/dist/comments/CommentThreadList.js.map +1 -1
  20. package/dist/data/CodeLine.cjs +4 -2
  21. package/dist/data/CodeLine.cjs.map +1 -1
  22. package/dist/data/CodeLine.d.ts.map +1 -1
  23. package/dist/data/CodeLine.js +4 -2
  24. package/dist/data/CodeLine.js.map +1 -1
  25. package/dist/data/DataTable.cjs +18 -8
  26. package/dist/data/DataTable.cjs.map +1 -1
  27. package/dist/data/DataTable.d.ts +4 -0
  28. package/dist/data/DataTable.d.ts.map +1 -1
  29. package/dist/data/DataTable.js +18 -8
  30. package/dist/data/DataTable.js.map +1 -1
  31. package/dist/data/SymbolBadge.cjs +61 -0
  32. package/dist/data/SymbolBadge.cjs.map +1 -0
  33. package/dist/data/SymbolBadge.d.ts +21 -0
  34. package/dist/data/SymbolBadge.d.ts.map +1 -0
  35. package/dist/data/SymbolBadge.js +61 -0
  36. package/dist/data/SymbolBadge.js.map +1 -0
  37. package/dist/data/TaskManager.cjs +183 -159
  38. package/dist/data/TaskManager.cjs.map +1 -1
  39. package/dist/data/TaskManager.d.ts +3 -1
  40. package/dist/data/TaskManager.d.ts.map +1 -1
  41. package/dist/data/TaskManager.js +185 -161
  42. package/dist/data/TaskManager.js.map +1 -1
  43. package/dist/data/TaskManagerButton.cjs +1 -0
  44. package/dist/data/TaskManagerButton.cjs.map +1 -1
  45. package/dist/data/TaskManagerButton.js +1 -0
  46. package/dist/data/TaskManagerButton.js.map +1 -1
  47. package/dist/data/TaskProgress.cjs +13 -7
  48. package/dist/data/TaskProgress.cjs.map +1 -1
  49. package/dist/data/TaskProgress.d.ts +3 -1
  50. package/dist/data/TaskProgress.d.ts.map +1 -1
  51. package/dist/data/TaskProgress.js +13 -7
  52. package/dist/data/TaskProgress.js.map +1 -1
  53. package/dist/data/test-runner/TestDetailPanel.cjs +26 -12
  54. package/dist/data/test-runner/TestDetailPanel.cjs.map +1 -1
  55. package/dist/data/test-runner/TestDetailPanel.d.ts.map +1 -1
  56. package/dist/data/test-runner/TestDetailPanel.js +27 -13
  57. package/dist/data/test-runner/TestDetailPanel.js.map +1 -1
  58. package/dist/data/test-runner/TestRunner.cjs +6 -0
  59. package/dist/data/test-runner/TestRunner.cjs.map +1 -1
  60. package/dist/data/test-runner/TestRunner.d.ts +3 -1
  61. package/dist/data/test-runner/TestRunner.d.ts.map +1 -1
  62. package/dist/data/test-runner/TestRunner.js +6 -0
  63. package/dist/data/test-runner/TestRunner.js.map +1 -1
  64. package/dist/data/test-runner/TestTreeNode.cjs +2 -1
  65. package/dist/data/test-runner/TestTreeNode.cjs.map +1 -1
  66. package/dist/data/test-runner/TestTreeNode.d.ts.map +1 -1
  67. package/dist/data/test-runner/TestTreeNode.js +2 -1
  68. package/dist/data/test-runner/TestTreeNode.js.map +1 -1
  69. package/dist/data/test-runner/adapter.cjs +4 -0
  70. package/dist/data/test-runner/adapter.cjs.map +1 -1
  71. package/dist/data/test-runner/adapter.d.ts +9 -0
  72. package/dist/data/test-runner/adapter.d.ts.map +1 -1
  73. package/dist/data/test-runner/adapter.js +4 -0
  74. package/dist/data/test-runner/adapter.js.map +1 -1
  75. package/dist/data/test-runner/context.cjs.map +1 -1
  76. package/dist/data/test-runner/context.d.ts +2 -0
  77. package/dist/data/test-runner/context.d.ts.map +1 -1
  78. package/dist/data/test-runner/context.js.map +1 -1
  79. package/dist/data/test-runner/index.d.ts +2 -2
  80. package/dist/data/test-runner/index.d.ts.map +1 -1
  81. package/dist/data/test-runner/marks.cjs +42 -1
  82. package/dist/data/test-runner/marks.cjs.map +1 -1
  83. package/dist/data/test-runner/marks.d.ts +10 -0
  84. package/dist/data/test-runner/marks.d.ts.map +1 -1
  85. package/dist/data/test-runner/marks.js +42 -1
  86. package/dist/data/test-runner/marks.js.map +1 -1
  87. package/dist/data/traces/ProcessTraceTree.cjs +93 -0
  88. package/dist/data/traces/ProcessTraceTree.cjs.map +1 -0
  89. package/dist/data/traces/ProcessTraceTree.d.ts +24 -0
  90. package/dist/data/traces/ProcessTraceTree.d.ts.map +1 -0
  91. package/dist/data/traces/ProcessTraceTree.js +93 -0
  92. package/dist/data/traces/ProcessTraceTree.js.map +1 -0
  93. package/dist/data/traces/Sparkline.cjs +23 -0
  94. package/dist/data/traces/Sparkline.cjs.map +1 -0
  95. package/dist/data/traces/Sparkline.d.ts +13 -0
  96. package/dist/data/traces/Sparkline.d.ts.map +1 -0
  97. package/dist/data/traces/Sparkline.js +23 -0
  98. package/dist/data/traces/Sparkline.js.map +1 -0
  99. package/dist/data/traces/SqlTraceTable.cjs +104 -0
  100. package/dist/data/traces/SqlTraceTable.cjs.map +1 -0
  101. package/dist/data/traces/SqlTraceTable.d.ts +19 -0
  102. package/dist/data/traces/SqlTraceTable.d.ts.map +1 -0
  103. package/dist/data/traces/SqlTraceTable.js +104 -0
  104. package/dist/data/traces/SqlTraceTable.js.map +1 -0
  105. package/dist/data/traces/TerminalCastPlayer.cjs +162 -0
  106. package/dist/data/traces/TerminalCastPlayer.cjs.map +1 -0
  107. package/dist/data/traces/TerminalCastPlayer.d.ts +21 -0
  108. package/dist/data/traces/TerminalCastPlayer.d.ts.map +1 -0
  109. package/dist/data/traces/TerminalCastPlayer.js +162 -0
  110. package/dist/data/traces/TerminalCastPlayer.js.map +1 -0
  111. package/dist/data/traces/TraceHarView.cjs +15 -0
  112. package/dist/data/traces/TraceHarView.cjs.map +1 -0
  113. package/dist/data/traces/TraceHarView.d.ts +13 -0
  114. package/dist/data/traces/TraceHarView.d.ts.map +1 -0
  115. package/dist/data/traces/TraceHarView.js +15 -0
  116. package/dist/data/traces/TraceHarView.js.map +1 -0
  117. package/dist/data/traces/TraceView.cjs +54 -0
  118. package/dist/data/traces/TraceView.cjs.map +1 -0
  119. package/dist/data/traces/TraceView.d.ts +18 -0
  120. package/dist/data/traces/TraceView.d.ts.map +1 -0
  121. package/dist/data/traces/TraceView.js +54 -0
  122. package/dist/data/traces/TraceView.js.map +1 -0
  123. package/dist/data/traces/cast.cjs +81 -0
  124. package/dist/data/traces/cast.cjs.map +1 -0
  125. package/dist/data/traces/cast.d.ts +9 -0
  126. package/dist/data/traces/cast.d.ts.map +1 -0
  127. package/dist/data/traces/cast.js +81 -0
  128. package/dist/data/traces/cast.js.map +1 -0
  129. package/dist/data/traces/fixtures.d.ts +8 -0
  130. package/dist/data/traces/fixtures.d.ts.map +1 -0
  131. package/dist/data/traces/har-adapter.cjs +83 -0
  132. package/dist/data/traces/har-adapter.cjs.map +1 -0
  133. package/dist/data/traces/har-adapter.d.ts +13 -0
  134. package/dist/data/traces/har-adapter.d.ts.map +1 -0
  135. package/dist/data/traces/har-adapter.js +83 -0
  136. package/dist/data/traces/har-adapter.js.map +1 -0
  137. package/dist/data/traces/index.d.ts +12 -0
  138. package/dist/data/traces/index.d.ts.map +1 -0
  139. package/dist/data/traces/process-tree.cjs +53 -0
  140. package/dist/data/traces/process-tree.cjs.map +1 -0
  141. package/dist/data/traces/process-tree.d.ts +21 -0
  142. package/dist/data/traces/process-tree.d.ts.map +1 -0
  143. package/dist/data/traces/process-tree.js +53 -0
  144. package/dist/data/traces/process-tree.js.map +1 -0
  145. package/dist/data/traces/sql-trace.cjs +39 -0
  146. package/dist/data/traces/sql-trace.cjs.map +1 -0
  147. package/dist/data/traces/sql-trace.d.ts +31 -0
  148. package/dist/data/traces/sql-trace.d.ts.map +1 -0
  149. package/dist/data/traces/sql-trace.js +39 -0
  150. package/dist/data/traces/sql-trace.js.map +1 -0
  151. package/dist/data/traces/terminal-screen.cjs +270 -0
  152. package/dist/data/traces/terminal-screen.cjs.map +1 -0
  153. package/dist/data/traces/terminal-screen.d.ts +45 -0
  154. package/dist/data/traces/terminal-screen.d.ts.map +1 -0
  155. package/dist/data/traces/terminal-screen.js +270 -0
  156. package/dist/data/traces/terminal-screen.js.map +1 -0
  157. package/dist/data/traces/terminal-timeline.cjs +48 -0
  158. package/dist/data/traces/terminal-timeline.cjs.map +1 -0
  159. package/dist/data/traces/terminal-timeline.d.ts +26 -0
  160. package/dist/data/traces/terminal-timeline.d.ts.map +1 -0
  161. package/dist/data/traces/terminal-timeline.js +48 -0
  162. package/dist/data/traces/terminal-timeline.js.map +1 -0
  163. package/dist/data/traces/types.d.ts +139 -0
  164. package/dist/data/traces/types.d.ts.map +1 -0
  165. package/dist/data/verification/VerificationResults.cjs +4 -0
  166. package/dist/data/verification/VerificationResults.cjs.map +1 -1
  167. package/dist/data/verification/VerificationResults.d.ts +3 -1
  168. package/dist/data/verification/VerificationResults.d.ts.map +1 -1
  169. package/dist/data/verification/VerificationResults.js +4 -0
  170. package/dist/data/verification/VerificationResults.js.map +1 -1
  171. package/dist/data/verification/adapterViews.cjs +12 -3
  172. package/dist/data/verification/adapterViews.cjs.map +1 -1
  173. package/dist/data/verification/adapterViews.d.ts +4 -1
  174. package/dist/data/verification/adapterViews.d.ts.map +1 -1
  175. package/dist/data/verification/adapterViews.js +12 -3
  176. package/dist/data/verification/adapterViews.js.map +1 -1
  177. package/dist/data/verification/adapters.cjs +53 -10
  178. package/dist/data/verification/adapters.cjs.map +1 -1
  179. package/dist/data/verification/adapters.d.ts +7 -1
  180. package/dist/data/verification/adapters.d.ts.map +1 -1
  181. package/dist/data/verification/adapters.js +53 -10
  182. package/dist/data/verification/adapters.js.map +1 -1
  183. package/dist/data/verification/index.d.ts +4 -2
  184. package/dist/data/verification/index.d.ts.map +1 -1
  185. package/dist/data/verification/stepKind.cjs +217 -0
  186. package/dist/data/verification/stepKind.cjs.map +1 -0
  187. package/dist/data/verification/stepKind.d.ts +87 -0
  188. package/dist/data/verification/stepKind.d.ts.map +1 -0
  189. package/dist/data/verification/stepKind.js +217 -0
  190. package/dist/data/verification/stepKind.js.map +1 -0
  191. package/dist/data/verification/stepKindViews.cjs +111 -0
  192. package/dist/data/verification/stepKindViews.cjs.map +1 -0
  193. package/dist/data/verification/stepKindViews.d.ts +18 -0
  194. package/dist/data/verification/stepKindViews.d.ts.map +1 -0
  195. package/dist/data/verification/stepKindViews.js +111 -0
  196. package/dist/data/verification/stepKindViews.js.map +1 -0
  197. package/dist/data/verification/verifyReportTests.cjs +1 -0
  198. package/dist/data/verification/verifyReportTests.cjs.map +1 -1
  199. package/dist/data/verification/verifyReportTests.d.ts +2 -1
  200. package/dist/data/verification/verifyReportTests.d.ts.map +1 -1
  201. package/dist/data/verification/verifyReportTests.js +1 -0
  202. package/dist/data/verification/verifyReportTests.js.map +1 -1
  203. package/dist/data/version-info.cjs +3 -3
  204. package/dist/data/version-info.cjs.map +1 -1
  205. package/dist/data/version-info.js +3 -3
  206. package/dist/data/version-info.js.map +1 -1
  207. package/dist/data.cjs +36 -0
  208. package/dist/data.cjs.map +1 -1
  209. package/dist/data.d.ts +2 -0
  210. package/dist/data.d.ts.map +1 -1
  211. package/dist/data.js +38 -2
  212. package/dist/data.js.map +1 -1
  213. package/dist/index.cjs +38 -0
  214. package/dist/index.cjs.map +1 -1
  215. package/dist/index.js +40 -2
  216. package/dist/index.js.map +1 -1
  217. package/dist/rpc/FilterForm.cjs +9 -0
  218. package/dist/rpc/FilterForm.cjs.map +1 -1
  219. package/dist/rpc/FilterForm.d.ts +5 -1
  220. package/dist/rpc/FilterForm.d.ts.map +1 -1
  221. package/dist/rpc/FilterForm.js +9 -0
  222. package/dist/rpc/FilterForm.js.map +1 -1
  223. package/dist/rpc/OperationArgsForm.cjs +153 -0
  224. package/dist/rpc/OperationArgsForm.cjs.map +1 -0
  225. package/dist/rpc/OperationArgsForm.d.ts +16 -0
  226. package/dist/rpc/OperationArgsForm.d.ts.map +1 -0
  227. package/dist/rpc/OperationArgsForm.js +153 -0
  228. package/dist/rpc/OperationArgsForm.js.map +1 -0
  229. package/dist/rpc/OperationScheduleDialog.cjs +61 -17
  230. package/dist/rpc/OperationScheduleDialog.cjs.map +1 -1
  231. package/dist/rpc/OperationScheduleDialog.d.ts +3 -0
  232. package/dist/rpc/OperationScheduleDialog.d.ts.map +1 -1
  233. package/dist/rpc/OperationScheduleDialog.js +62 -18
  234. package/dist/rpc/OperationScheduleDialog.js.map +1 -1
  235. package/dist/rpc/OperationSchedules.cjs +6 -1
  236. package/dist/rpc/OperationSchedules.cjs.map +1 -1
  237. package/dist/rpc/OperationSchedules.d.ts.map +1 -1
  238. package/dist/rpc/OperationSchedules.js +6 -1
  239. package/dist/rpc/OperationSchedules.js.map +1 -1
  240. package/dist/rpc/operation-schedule-model.cjs +0 -7
  241. package/dist/rpc/operation-schedule-model.cjs.map +1 -1
  242. package/dist/rpc/operation-schedule-model.d.ts +0 -2
  243. package/dist/rpc/operation-schedule-model.d.ts.map +1 -1
  244. package/dist/rpc/operation-schedule-model.js +0 -7
  245. package/dist/rpc/operation-schedule-model.js.map +1 -1
  246. package/dist/rpc/operation-schedule-types.d.ts +6 -1
  247. package/dist/rpc/operation-schedule-types.d.ts.map +1 -1
  248. package/dist/rpc.cjs +2 -0
  249. package/dist/rpc.cjs.map +1 -1
  250. package/dist/rpc.d.ts +1 -0
  251. package/dist/rpc.d.ts.map +1 -1
  252. package/dist/rpc.js +2 -0
  253. package/dist/rpc.js.map +1 -1
  254. package/dist/styles.css +1 -1
  255. package/package.json +1 -1
@@ -21,12 +21,18 @@ const UiLogoOxc = require("../../icons/components/UiLogoOxc.cjs");
21
21
  const UiLogoEslint = require("../../icons/components/UiLogoEslint.cjs");
22
22
  const UiLogoGo = require("../../icons/components/UiLogoGo.cjs");
23
23
  const UiSealCheck = require("../../icons/components/UiSealCheck.cjs");
24
+ const UiCursorClick = require("../../icons/components/UiCursorClick.cjs");
25
+ const UiLogoPlaywright = require("../../icons/components/UiLogoPlaywright.cjs");
26
+ const UiGlobe = require("../../icons/components/UiGlobe.cjs");
27
+ const UiRobotAi = require("../../icons/components/UiRobotAi.cjs");
28
+ const UiChatDots = require("../../icons/components/UiChatDots.cjs");
29
+ const UiClipboardText = require("../../icons/components/UiClipboardText.cjs");
30
+ const UiServer = require("../../icons/components/UiServer.cjs");
24
31
  const UiDatabase = require("../../icons/components/UiDatabase.cjs");
25
32
  const UiSparkles = require("../../icons/components/UiSparkles.cjs");
26
33
  const UiListChecks = require("../../icons/components/UiListChecks.cjs");
27
34
  const UiBeaker = require("../../icons/components/UiBeaker.cjs");
28
35
  const UiTerminal = require("../../icons/components/UiTerminal.cjs");
29
- const UiLogoPlaywright = require("../../icons/components/UiLogoPlaywright.cjs");
30
36
  const UiLogoJest = require("../../icons/components/UiLogoJest.cjs");
31
37
  const UiLogoVitest = require("../../icons/components/UiLogoVitest.cjs");
32
38
  const LABEL = Object.fromEntries(status.STATUS_LABELS.map((s) => [s.key, s.label]));
@@ -88,6 +94,31 @@ const TEST_TOOLS = {
88
94
  className: "text-violet-600 dark:text-violet-400"
89
95
  },
90
96
  query: { icon: UiDatabase.UiDatabase, label: "Query", kind: "fixture", className: MUTED },
97
+ // Typed step kinds (gavel fixtures/kind registry): the node's `framework`
98
+ // is the kind name. `ai` is the runner behind `verify` but is a prompt/CEL
99
+ // round-trip rather than an acceptance-criteria review, so it takes the
100
+ // same violet family with its own glyph and label.
101
+ services: { icon: UiServer.UiServer, label: "Services", kind: "fixture", className: MUTED },
102
+ todo: { icon: UiClipboardText.UiClipboardText, label: "TODO step", kind: "fixture", className: MUTED },
103
+ todo_prompt: { icon: UiChatDots.UiChatDots, label: "TODO prompt run", kind: "fixture", className: MUTED },
104
+ ai: {
105
+ icon: UiRobotAi.UiRobotAi,
106
+ label: "AI step",
107
+ kind: "fixture",
108
+ className: "text-violet-600 dark:text-violet-400"
109
+ },
110
+ browser: { icon: UiGlobe.UiGlobe, label: "Browser", kind: "fixture", className: MUTED },
111
+ // The fixture-step twin of the `playwright` test framework above; both
112
+ // arrive as framework "playwright", so toolKeyFor() picks this key when the
113
+ // node carries `detail.step`. Muted like every other fixture step, so the
114
+ // brand green stays on the spec results the framework reports.
115
+ "playwright-step": {
116
+ icon: UiLogoPlaywright.UiLogoPlaywright,
117
+ label: "Playwright step",
118
+ kind: "fixture",
119
+ className: MUTED
120
+ },
121
+ "agent-browser": { icon: UiCursorClick.UiCursorClick, label: "Agent browser", kind: "fixture", className: MUTED },
91
122
  criterion: { icon: UiSealCheck.UiSealCheck, label: "Acceptance criterion", kind: "fixture", className: MUTED },
92
123
  checklist: { icon: UiSealCheck.UiSealCheck, label: "Acceptance criteria", kind: "fixture", className: MUTED },
93
124
  "golangci-lint": { icon: UiLogoGo.UiLogoGo, label: "golangci-lint", kind: "lint", className: GO },
@@ -136,8 +167,18 @@ const TEST_TOOLS = {
136
167
  function toolMark(tool) {
137
168
  return tool && TEST_TOOLS[tool] || null;
138
169
  }
170
+ function hasStepInfo(detail) {
171
+ if (detail === null || typeof detail !== "object" || Array.isArray(detail)) return false;
172
+ const step = detail.step;
173
+ return step !== null && typeof step === "object" && !Array.isArray(step);
174
+ }
175
+ function toolKeyFor(node) {
176
+ if (node.framework === "playwright" && hasStepInfo(node.detail)) return "playwright-step";
177
+ return node.framework;
178
+ }
139
179
  exports.LINT_SEVERITIES = LINT_SEVERITIES;
140
180
  exports.TEST_OUTCOMES = TEST_OUTCOMES;
141
181
  exports.TEST_TOOLS = TEST_TOOLS;
182
+ exports.toolKeyFor = toolKeyFor;
142
183
  exports.toolMark = toolMark;
143
184
  //# sourceMappingURL=marks.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"marks.cjs","sources":["../../../src/data/test-runner/marks.ts"],"sourcesContent":["// The shared iconography for test and lint results: what happened (outcome),\n// how bad a finding is (lint severity), and what produced it (tool).\n//\n// Every outcome has its own outline glyph, so a result never reads by colour\n// alone.\n// Tool marks are bundled Simple Icons brand glyphs (offline, no runtime icon\n// names), coloured per theme here because one brand hex can't hold up on both\n// light and dark surfaces.\n\nimport type { StaticIconComponent } from \"../Icon\";\nimport {\n UiCircleOutline,\n UiCircleX,\n UiBeaker,\n UiCopy,\n UiDatabase,\n UiInfo,\n UiKey,\n UiListChecks,\n UiLoader,\n UiLogoEslint,\n UiLogoGo,\n UiLogoJest,\n UiLogoMarkdown,\n UiLogoOxc,\n UiLogoPlaywright,\n UiLogoPython,\n UiLogoReact,\n UiLogoRuff,\n UiLogoTypescript,\n UiLogoVitest,\n UiPass,\n UiProhibit,\n UiSealCheck,\n UiSkipForward,\n UiSparkles,\n UiTerminal,\n UiTimer,\n UiWarningTriangle,\n} from \"../../icons\";\nimport { STATUS_LABELS, type TestOutcome } from \"./status\";\n\nexport type { TestOutcome } from \"./status\";\n\nexport type ResultMark = {\n icon: StaticIconComponent;\n label: string;\n /** Text colour classes for light and dark themes. */\n className: string;\n spin?: boolean;\n};\n\nconst LABEL = Object.fromEntries(STATUS_LABELS.map((s) => [s.key, s.label]));\n\nconst RED = \"text-red-600 dark:text-red-400\";\nconst AMBER = \"text-amber-600 dark:text-amber-400\";\n\nexport const TEST_OUTCOMES: Record<TestOutcome, ResultMark> = {\n failed: { icon: UiCircleX, label: LABEL.failed!, className: RED },\n errored: { icon: UiProhibit, label: \"Could not run\", className: RED },\n timedout: { icon: UiTimer, label: LABEL.timedout!, className: AMBER },\n warned: { icon: UiWarningTriangle, label: LABEL.warned!, className: AMBER },\n passed: {\n icon: UiPass,\n label: LABEL.passed!,\n className: \"text-emerald-600 dark:text-emerald-400\",\n },\n skipped: { icon: UiSkipForward, label: LABEL.skipped!, className: \"text-slate-500\" },\n pending: {\n icon: UiCircleOutline,\n label: LABEL.pending!,\n className: \"text-slate-400 dark:text-slate-500\",\n },\n running: {\n icon: UiLoader,\n label: LABEL.running!,\n className: \"text-blue-600 dark:text-blue-400\",\n spin: true,\n },\n};\n\nexport type LintSeverity = \"error\" | \"warning\" | \"info\";\n\n/** An error-severity finding fails the verdict and a warning warns it, so they share those outcomes' marks. */\nexport const LINT_SEVERITIES: Record<LintSeverity, ResultMark> = {\n error: { ...TEST_OUTCOMES.failed, label: \"Error\" },\n warning: { ...TEST_OUTCOMES.warned, label: \"Warning\" },\n info: { icon: UiInfo, label: \"Info\", className: \"text-sky-600 dark:text-sky-400\" },\n};\n\nexport type ToolKind = \"test\" | \"lint\" | \"fixture\" | \"runner\";\n\nexport type ToolMark = {\n icon: StaticIconComponent;\n label: string;\n kind: ToolKind;\n className: string;\n};\n\nconst GO = \"text-[#00add8]\";\nconst MARKDOWN = \"text-foreground\";\nconst MUTED = \"text-muted-foreground\";\n\n/**\n * Keyed by the identifiers gavel reports: testrunner/parsers Framework values\n * and each linter's Name(). A tool without a mark of its own takes its\n * language's (golangci-lint → Go, pyright → Python).\n */\nexport const TEST_TOOLS: Record<string, ToolMark> = {\n \"go test\": { icon: UiLogoGo, label: \"go test\", kind: \"test\", className: GO },\n ginkgo: { icon: UiLogoGo, label: \"Ginkgo\", kind: \"test\", className: GO },\n vitest: { icon: UiLogoVitest, label: \"Vitest\", kind: \"test\", className: \"text-[#6e9f18]\" },\n jest: { icon: UiLogoJest, label: \"Jest\", kind: \"test\", className: \"text-[#c21325] dark:text-[#e5576a]\" },\n playwright: {\n icon: UiLogoPlaywright,\n label: \"Playwright\",\n kind: \"test\",\n className: \"text-[#2ead33]\",\n },\n fixture: { icon: UiLogoMarkdown, label: \"Markdown fixture\", kind: \"test\", className: MARKDOWN },\n task: { icon: UiTerminal, label: \"Task\", kind: \"runner\", className: MUTED },\n // Fixture step types (gavel fixtures FixtureResult.Type) and the\n // \"checklist\" node verifyReportTests synthesizes for acceptance criteria.\n exec: { icon: UiTerminal, label: \"Command\", kind: \"fixture\", className: MUTED },\n test: { icon: UiBeaker, label: \"Test step\", kind: \"fixture\", className: MUTED },\n lint: { icon: UiListChecks, label: \"Lint step\", kind: \"fixture\", className: MUTED },\n verify: {\n icon: UiSparkles,\n label: \"AI review\",\n kind: \"fixture\",\n className: \"text-violet-600 dark:text-violet-400\",\n },\n query: { icon: UiDatabase, label: \"Query\", kind: \"fixture\", className: MUTED },\n criterion: { icon: UiSealCheck, label: \"Acceptance criterion\", kind: \"fixture\", className: MUTED },\n checklist: { icon: UiSealCheck, label: \"Acceptance criteria\", kind: \"fixture\", className: MUTED },\n \"golangci-lint\": { icon: UiLogoGo, label: \"golangci-lint\", kind: \"lint\", className: GO },\n eslint: {\n icon: UiLogoEslint,\n label: \"ESLint\",\n kind: \"lint\",\n className: \"text-[#4b32c3] dark:text-[#8080f2]\",\n },\n oxlint: {\n icon: UiLogoOxc,\n label: \"oxlint\",\n kind: \"lint\",\n className: \"text-[#0891b2] dark:text-[#00f7f1]\",\n },\n tsc: {\n icon: UiLogoTypescript,\n label: \"TypeScript compiler\",\n kind: \"lint\",\n className: \"text-[#3178c6]\",\n },\n markdownlint: { icon: UiLogoMarkdown, label: \"markdownlint\", kind: \"lint\", className: MARKDOWN },\n vale: { icon: UiLogoMarkdown, label: \"Vale (prose)\", kind: \"lint\", className: MARKDOWN },\n pyright: {\n icon: UiLogoPython,\n label: \"Pyright\",\n kind: \"lint\",\n className: \"text-[#3776ab] dark:text-[#ffd43b]\",\n },\n ruff: {\n icon: UiLogoRuff,\n label: \"Ruff\",\n kind: \"lint\",\n className: \"text-[#261230] dark:text-[#d7ff64]\",\n },\n // A secret scanner's finding is about a credential, not a language.\n betterleaks: { icon: UiKey, label: \"betterleaks (secrets)\", kind: \"lint\", className: MUTED },\n jscpd: { icon: UiCopy, label: \"jscpd (duplicate code)\", kind: \"lint\", className: MUTED },\n reactdoctor: {\n icon: UiLogoReact,\n label: \"React Doctor\",\n kind: \"lint\",\n className: \"text-[#0e7490] dark:text-[#61dafb]\",\n },\n};\n\n/** The tool's mark, or null for a tool the catalog doesn't know (render its name alone). */\nexport function toolMark(tool: string | undefined): ToolMark | null {\n return (tool && TEST_TOOLS[tool]) || null;\n}\n\n"],"names":["STATUS_LABELS","UiCircleX","UiProhibit","UiTimer","UiWarningTriangle","UiPass","UiSkipForward","UiCircleOutline","UiLoader","UiInfo","UiLogoGo","UiLogoVitest","UiLogoJest","UiLogoPlaywright","UiLogoMarkdown","UiTerminal","UiBeaker","UiListChecks","UiSparkles","UiDatabase","UiSealCheck","UiLogoEslint","UiLogoOxc","UiLogoTypescript","UiLogoPython","UiLogoRuff","UiKey","UiCopy","UiLogoReact"],"mappings":"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"}
1
+ {"version":3,"file":"marks.cjs","sources":["../../../src/data/test-runner/marks.ts"],"sourcesContent":["// The shared iconography for test and lint results: what happened (outcome),\n// how bad a finding is (lint severity), and what produced it (tool).\n//\n// Every outcome has its own outline glyph, so a result never reads by colour\n// alone.\n// Tool marks are bundled Simple Icons brand glyphs (offline, no runtime icon\n// names), coloured per theme here because one brand hex can't hold up on both\n// light and dark surfaces.\n\nimport type { StaticIconComponent } from \"../Icon\";\nimport {\n UiCircleOutline,\n UiCircleX,\n UiBeaker,\n UiChatDots,\n UiClipboardText,\n UiCopy,\n UiCursorClick,\n UiDatabase,\n UiGlobe,\n UiInfo,\n UiKey,\n UiListChecks,\n UiLoader,\n UiLogoEslint,\n UiLogoGo,\n UiLogoJest,\n UiLogoMarkdown,\n UiLogoOxc,\n UiLogoPlaywright,\n UiLogoPython,\n UiLogoReact,\n UiLogoRuff,\n UiLogoTypescript,\n UiLogoVitest,\n UiPass,\n UiProhibit,\n UiRobotAi,\n UiSealCheck,\n UiServer,\n UiSkipForward,\n UiSparkles,\n UiTerminal,\n UiTimer,\n UiWarningTriangle,\n} from \"../../icons\";\nimport { STATUS_LABELS, type TestOutcome } from \"./status\";\n\nexport type { TestOutcome } from \"./status\";\n\nexport type ResultMark = {\n icon: StaticIconComponent;\n label: string;\n /** Text colour classes for light and dark themes. */\n className: string;\n spin?: boolean;\n};\n\nconst LABEL = Object.fromEntries(STATUS_LABELS.map((s) => [s.key, s.label]));\n\nconst RED = \"text-red-600 dark:text-red-400\";\nconst AMBER = \"text-amber-600 dark:text-amber-400\";\n\nexport const TEST_OUTCOMES: Record<TestOutcome, ResultMark> = {\n failed: { icon: UiCircleX, label: LABEL.failed!, className: RED },\n errored: { icon: UiProhibit, label: \"Could not run\", className: RED },\n timedout: { icon: UiTimer, label: LABEL.timedout!, className: AMBER },\n warned: { icon: UiWarningTriangle, label: LABEL.warned!, className: AMBER },\n passed: {\n icon: UiPass,\n label: LABEL.passed!,\n className: \"text-emerald-600 dark:text-emerald-400\",\n },\n skipped: { icon: UiSkipForward, label: LABEL.skipped!, className: \"text-slate-500\" },\n pending: {\n icon: UiCircleOutline,\n label: LABEL.pending!,\n className: \"text-slate-400 dark:text-slate-500\",\n },\n running: {\n icon: UiLoader,\n label: LABEL.running!,\n className: \"text-blue-600 dark:text-blue-400\",\n spin: true,\n },\n};\n\nexport type LintSeverity = \"error\" | \"warning\" | \"info\";\n\n/** An error-severity finding fails the verdict and a warning warns it, so they share those outcomes' marks. */\nexport const LINT_SEVERITIES: Record<LintSeverity, ResultMark> = {\n error: { ...TEST_OUTCOMES.failed, label: \"Error\" },\n warning: { ...TEST_OUTCOMES.warned, label: \"Warning\" },\n info: { icon: UiInfo, label: \"Info\", className: \"text-sky-600 dark:text-sky-400\" },\n};\n\nexport type ToolKind = \"test\" | \"lint\" | \"fixture\" | \"runner\";\n\nexport type ToolMark = {\n icon: StaticIconComponent;\n label: string;\n kind: ToolKind;\n className: string;\n};\n\nconst GO = \"text-[#00add8]\";\nconst MARKDOWN = \"text-foreground\";\nconst MUTED = \"text-muted-foreground\";\n\n/**\n * Keyed by the identifiers gavel reports: testrunner/parsers Framework values\n * and each linter's Name(). A tool without a mark of its own takes its\n * language's (golangci-lint → Go, pyright → Python).\n */\nexport const TEST_TOOLS: Record<string, ToolMark> = {\n \"go test\": { icon: UiLogoGo, label: \"go test\", kind: \"test\", className: GO },\n ginkgo: { icon: UiLogoGo, label: \"Ginkgo\", kind: \"test\", className: GO },\n vitest: { icon: UiLogoVitest, label: \"Vitest\", kind: \"test\", className: \"text-[#6e9f18]\" },\n jest: { icon: UiLogoJest, label: \"Jest\", kind: \"test\", className: \"text-[#c21325] dark:text-[#e5576a]\" },\n playwright: {\n icon: UiLogoPlaywright,\n label: \"Playwright\",\n kind: \"test\",\n className: \"text-[#2ead33]\",\n },\n fixture: { icon: UiLogoMarkdown, label: \"Markdown fixture\", kind: \"test\", className: MARKDOWN },\n task: { icon: UiTerminal, label: \"Task\", kind: \"runner\", className: MUTED },\n // Fixture step types (gavel fixtures FixtureResult.Type) and the\n // \"checklist\" node verifyReportTests synthesizes for acceptance criteria.\n exec: { icon: UiTerminal, label: \"Command\", kind: \"fixture\", className: MUTED },\n test: { icon: UiBeaker, label: \"Test step\", kind: \"fixture\", className: MUTED },\n lint: { icon: UiListChecks, label: \"Lint step\", kind: \"fixture\", className: MUTED },\n verify: {\n icon: UiSparkles,\n label: \"AI review\",\n kind: \"fixture\",\n className: \"text-violet-600 dark:text-violet-400\",\n },\n query: { icon: UiDatabase, label: \"Query\", kind: \"fixture\", className: MUTED },\n // Typed step kinds (gavel fixtures/kind registry): the node's `framework`\n // is the kind name. `ai` is the runner behind `verify` but is a prompt/CEL\n // round-trip rather than an acceptance-criteria review, so it takes the\n // same violet family with its own glyph and label.\n services: { icon: UiServer, label: \"Services\", kind: \"fixture\", className: MUTED },\n todo: { icon: UiClipboardText, label: \"TODO step\", kind: \"fixture\", className: MUTED },\n todo_prompt: { icon: UiChatDots, label: \"TODO prompt run\", kind: \"fixture\", className: MUTED },\n ai: {\n icon: UiRobotAi,\n label: \"AI step\",\n kind: \"fixture\",\n className: \"text-violet-600 dark:text-violet-400\",\n },\n browser: { icon: UiGlobe, label: \"Browser\", kind: \"fixture\", className: MUTED },\n // The fixture-step twin of the `playwright` test framework above; both\n // arrive as framework \"playwright\", so toolKeyFor() picks this key when the\n // node carries `detail.step`. Muted like every other fixture step, so the\n // brand green stays on the spec results the framework reports.\n \"playwright-step\": {\n icon: UiLogoPlaywright,\n label: \"Playwright step\",\n kind: \"fixture\",\n className: MUTED,\n },\n \"agent-browser\": { icon: UiCursorClick, label: \"Agent browser\", kind: \"fixture\", className: MUTED },\n criterion: { icon: UiSealCheck, label: \"Acceptance criterion\", kind: \"fixture\", className: MUTED },\n checklist: { icon: UiSealCheck, label: \"Acceptance criteria\", kind: \"fixture\", className: MUTED },\n \"golangci-lint\": { icon: UiLogoGo, label: \"golangci-lint\", kind: \"lint\", className: GO },\n eslint: {\n icon: UiLogoEslint,\n label: \"ESLint\",\n kind: \"lint\",\n className: \"text-[#4b32c3] dark:text-[#8080f2]\",\n },\n oxlint: {\n icon: UiLogoOxc,\n label: \"oxlint\",\n kind: \"lint\",\n className: \"text-[#0891b2] dark:text-[#00f7f1]\",\n },\n tsc: {\n icon: UiLogoTypescript,\n label: \"TypeScript compiler\",\n kind: \"lint\",\n className: \"text-[#3178c6]\",\n },\n markdownlint: { icon: UiLogoMarkdown, label: \"markdownlint\", kind: \"lint\", className: MARKDOWN },\n vale: { icon: UiLogoMarkdown, label: \"Vale (prose)\", kind: \"lint\", className: MARKDOWN },\n pyright: {\n icon: UiLogoPython,\n label: \"Pyright\",\n kind: \"lint\",\n className: \"text-[#3776ab] dark:text-[#ffd43b]\",\n },\n ruff: {\n icon: UiLogoRuff,\n label: \"Ruff\",\n kind: \"lint\",\n className: \"text-[#261230] dark:text-[#d7ff64]\",\n },\n // A secret scanner's finding is about a credential, not a language.\n betterleaks: { icon: UiKey, label: \"betterleaks (secrets)\", kind: \"lint\", className: MUTED },\n jscpd: { icon: UiCopy, label: \"jscpd (duplicate code)\", kind: \"lint\", className: MUTED },\n reactdoctor: {\n icon: UiLogoReact,\n label: \"React Doctor\",\n kind: \"lint\",\n className: \"text-[#0e7490] dark:text-[#61dafb]\",\n },\n};\n\n/** The tool's mark, or null for a tool the catalog doesn't know (render its name alone). */\nexport function toolMark(tool: string | undefined): ToolMark | null {\n return (tool && TEST_TOOLS[tool]) || null;\n}\n\nfunction hasStepInfo(detail: unknown): boolean {\n if (detail === null || typeof detail !== \"object\" || Array.isArray(detail)) return false;\n const step = (detail as Record<string, unknown>).step;\n return step !== null && typeof step === \"object\" && !Array.isArray(step);\n}\n\n/**\n * The TEST_TOOLS key for a node. A node's `framework` is the key, except that\n * \"playwright\" names both the test framework and the `playwright` fixture-step\n * kind: a node carrying `detail.step` (the fixture verifier's step id and\n * kind) is the step.\n */\nexport function toolKeyFor(node: { framework?: string | undefined; detail?: unknown }): string | undefined {\n if (node.framework === \"playwright\" && hasStepInfo(node.detail)) return \"playwright-step\";\n return node.framework;\n}\n\n"],"names":["STATUS_LABELS","UiCircleX","UiProhibit","UiTimer","UiWarningTriangle","UiPass","UiSkipForward","UiCircleOutline","UiLoader","UiInfo","UiLogoGo","UiLogoVitest","UiLogoJest","UiLogoPlaywright","UiLogoMarkdown","UiTerminal","UiBeaker","UiListChecks","UiSparkles","UiDatabase","UiServer","UiClipboardText","UiChatDots","UiRobotAi","UiGlobe","UiCursorClick","UiSealCheck","UiLogoEslint","UiLogoOxc","UiLogoTypescript","UiLogoPython","UiLogoRuff","UiKey","UiCopy","UiLogoReact"],"mappings":"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"}
@@ -27,4 +27,14 @@ export type ToolMark = {
27
27
  export declare const TEST_TOOLS: Record<string, ToolMark>;
28
28
  /** The tool's mark, or null for a tool the catalog doesn't know (render its name alone). */
29
29
  export declare function toolMark(tool: string | undefined): ToolMark | null;
30
+ /**
31
+ * The TEST_TOOLS key for a node. A node's `framework` is the key, except that
32
+ * "playwright" names both the test framework and the `playwright` fixture-step
33
+ * kind: a node carrying `detail.step` (the fixture verifier's step id and
34
+ * kind) is the step.
35
+ */
36
+ export declare function toolKeyFor(node: {
37
+ framework?: string | undefined;
38
+ detail?: unknown;
39
+ }): string | undefined;
30
40
  //# sourceMappingURL=marks.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"marks.d.ts","sourceRoot":"","sources":["../../../src/data/test-runner/marks.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AA+BnD,OAAO,EAAiB,KAAK,WAAW,EAAE,MAAM,UAAU,CAAC;AAE3D,YAAY,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAE5C,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,mBAAmB,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,qDAAqD;IACrD,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB,CAAC;AAOF,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,WAAW,EAAE,UAAU,CAsBzD,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;AAExD,+GAA+G;AAC/G,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,YAAY,EAAE,UAAU,CAI5D,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,CAAC;AAE9D,MAAM,MAAM,QAAQ,GAAG;IACrB,IAAI,EAAE,mBAAmB,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,QAAQ,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAMF;;;;GAIG;AACH,eAAO,MAAM,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAqE/C,CAAC;AAEF,4FAA4F;AAC5F,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,QAAQ,GAAG,IAAI,CAElE"}
1
+ {"version":3,"file":"marks.d.ts","sourceRoot":"","sources":["../../../src/data/test-runner/marks.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAqCnD,OAAO,EAAiB,KAAK,WAAW,EAAE,MAAM,UAAU,CAAC;AAE3D,YAAY,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAE5C,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,mBAAmB,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,qDAAqD;IACrD,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB,CAAC;AAOF,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,WAAW,EAAE,UAAU,CAsBzD,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;AAExD,+GAA+G;AAC/G,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,YAAY,EAAE,UAAU,CAI5D,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,CAAC;AAE9D,MAAM,MAAM,QAAQ,GAAG;IACrB,IAAI,EAAE,mBAAmB,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,QAAQ,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAMF;;;;GAIG;AACH,eAAO,MAAM,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CA8F/C,CAAC;AAEF,4FAA4F;AAC5F,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,QAAQ,GAAG,IAAI,CAElE;AAQD;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,MAAM,GAAG,SAAS,CAGzG"}
@@ -19,12 +19,18 @@ import { UiLogoOxc } from "../../icons/components/UiLogoOxc.js";
19
19
  import { UiLogoEslint } from "../../icons/components/UiLogoEslint.js";
20
20
  import { UiLogoGo } from "../../icons/components/UiLogoGo.js";
21
21
  import { UiSealCheck } from "../../icons/components/UiSealCheck.js";
22
+ import { UiCursorClick } from "../../icons/components/UiCursorClick.js";
23
+ import { UiLogoPlaywright } from "../../icons/components/UiLogoPlaywright.js";
24
+ import { UiGlobe } from "../../icons/components/UiGlobe.js";
25
+ import { UiRobotAi } from "../../icons/components/UiRobotAi.js";
26
+ import { UiChatDots } from "../../icons/components/UiChatDots.js";
27
+ import { UiClipboardText } from "../../icons/components/UiClipboardText.js";
28
+ import { UiServer } from "../../icons/components/UiServer.js";
22
29
  import { UiDatabase } from "../../icons/components/UiDatabase.js";
23
30
  import { UiSparkles } from "../../icons/components/UiSparkles.js";
24
31
  import { UiListChecks } from "../../icons/components/UiListChecks.js";
25
32
  import { UiBeaker } from "../../icons/components/UiBeaker.js";
26
33
  import { UiTerminal } from "../../icons/components/UiTerminal.js";
27
- import { UiLogoPlaywright } from "../../icons/components/UiLogoPlaywright.js";
28
34
  import { UiLogoJest } from "../../icons/components/UiLogoJest.js";
29
35
  import { UiLogoVitest } from "../../icons/components/UiLogoVitest.js";
30
36
  const LABEL = Object.fromEntries(STATUS_LABELS.map((s) => [s.key, s.label]));
@@ -86,6 +92,31 @@ const TEST_TOOLS = {
86
92
  className: "text-violet-600 dark:text-violet-400"
87
93
  },
88
94
  query: { icon: UiDatabase, label: "Query", kind: "fixture", className: MUTED },
95
+ // Typed step kinds (gavel fixtures/kind registry): the node's `framework`
96
+ // is the kind name. `ai` is the runner behind `verify` but is a prompt/CEL
97
+ // round-trip rather than an acceptance-criteria review, so it takes the
98
+ // same violet family with its own glyph and label.
99
+ services: { icon: UiServer, label: "Services", kind: "fixture", className: MUTED },
100
+ todo: { icon: UiClipboardText, label: "TODO step", kind: "fixture", className: MUTED },
101
+ todo_prompt: { icon: UiChatDots, label: "TODO prompt run", kind: "fixture", className: MUTED },
102
+ ai: {
103
+ icon: UiRobotAi,
104
+ label: "AI step",
105
+ kind: "fixture",
106
+ className: "text-violet-600 dark:text-violet-400"
107
+ },
108
+ browser: { icon: UiGlobe, label: "Browser", kind: "fixture", className: MUTED },
109
+ // The fixture-step twin of the `playwright` test framework above; both
110
+ // arrive as framework "playwright", so toolKeyFor() picks this key when the
111
+ // node carries `detail.step`. Muted like every other fixture step, so the
112
+ // brand green stays on the spec results the framework reports.
113
+ "playwright-step": {
114
+ icon: UiLogoPlaywright,
115
+ label: "Playwright step",
116
+ kind: "fixture",
117
+ className: MUTED
118
+ },
119
+ "agent-browser": { icon: UiCursorClick, label: "Agent browser", kind: "fixture", className: MUTED },
89
120
  criterion: { icon: UiSealCheck, label: "Acceptance criterion", kind: "fixture", className: MUTED },
90
121
  checklist: { icon: UiSealCheck, label: "Acceptance criteria", kind: "fixture", className: MUTED },
91
122
  "golangci-lint": { icon: UiLogoGo, label: "golangci-lint", kind: "lint", className: GO },
@@ -134,10 +165,20 @@ const TEST_TOOLS = {
134
165
  function toolMark(tool) {
135
166
  return tool && TEST_TOOLS[tool] || null;
136
167
  }
168
+ function hasStepInfo(detail) {
169
+ if (detail === null || typeof detail !== "object" || Array.isArray(detail)) return false;
170
+ const step = detail.step;
171
+ return step !== null && typeof step === "object" && !Array.isArray(step);
172
+ }
173
+ function toolKeyFor(node) {
174
+ if (node.framework === "playwright" && hasStepInfo(node.detail)) return "playwright-step";
175
+ return node.framework;
176
+ }
137
177
  export {
138
178
  LINT_SEVERITIES,
139
179
  TEST_OUTCOMES,
140
180
  TEST_TOOLS,
181
+ toolKeyFor,
141
182
  toolMark
142
183
  };
143
184
  //# sourceMappingURL=marks.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"marks.js","sources":["../../../src/data/test-runner/marks.ts"],"sourcesContent":["// The shared iconography for test and lint results: what happened (outcome),\n// how bad a finding is (lint severity), and what produced it (tool).\n//\n// Every outcome has its own outline glyph, so a result never reads by colour\n// alone.\n// Tool marks are bundled Simple Icons brand glyphs (offline, no runtime icon\n// names), coloured per theme here because one brand hex can't hold up on both\n// light and dark surfaces.\n\nimport type { StaticIconComponent } from \"../Icon\";\nimport {\n UiCircleOutline,\n UiCircleX,\n UiBeaker,\n UiCopy,\n UiDatabase,\n UiInfo,\n UiKey,\n UiListChecks,\n UiLoader,\n UiLogoEslint,\n UiLogoGo,\n UiLogoJest,\n UiLogoMarkdown,\n UiLogoOxc,\n UiLogoPlaywright,\n UiLogoPython,\n UiLogoReact,\n UiLogoRuff,\n UiLogoTypescript,\n UiLogoVitest,\n UiPass,\n UiProhibit,\n UiSealCheck,\n UiSkipForward,\n UiSparkles,\n UiTerminal,\n UiTimer,\n UiWarningTriangle,\n} from \"../../icons\";\nimport { STATUS_LABELS, type TestOutcome } from \"./status\";\n\nexport type { TestOutcome } from \"./status\";\n\nexport type ResultMark = {\n icon: StaticIconComponent;\n label: string;\n /** Text colour classes for light and dark themes. */\n className: string;\n spin?: boolean;\n};\n\nconst LABEL = Object.fromEntries(STATUS_LABELS.map((s) => [s.key, s.label]));\n\nconst RED = \"text-red-600 dark:text-red-400\";\nconst AMBER = \"text-amber-600 dark:text-amber-400\";\n\nexport const TEST_OUTCOMES: Record<TestOutcome, ResultMark> = {\n failed: { icon: UiCircleX, label: LABEL.failed!, className: RED },\n errored: { icon: UiProhibit, label: \"Could not run\", className: RED },\n timedout: { icon: UiTimer, label: LABEL.timedout!, className: AMBER },\n warned: { icon: UiWarningTriangle, label: LABEL.warned!, className: AMBER },\n passed: {\n icon: UiPass,\n label: LABEL.passed!,\n className: \"text-emerald-600 dark:text-emerald-400\",\n },\n skipped: { icon: UiSkipForward, label: LABEL.skipped!, className: \"text-slate-500\" },\n pending: {\n icon: UiCircleOutline,\n label: LABEL.pending!,\n className: \"text-slate-400 dark:text-slate-500\",\n },\n running: {\n icon: UiLoader,\n label: LABEL.running!,\n className: \"text-blue-600 dark:text-blue-400\",\n spin: true,\n },\n};\n\nexport type LintSeverity = \"error\" | \"warning\" | \"info\";\n\n/** An error-severity finding fails the verdict and a warning warns it, so they share those outcomes' marks. */\nexport const LINT_SEVERITIES: Record<LintSeverity, ResultMark> = {\n error: { ...TEST_OUTCOMES.failed, label: \"Error\" },\n warning: { ...TEST_OUTCOMES.warned, label: \"Warning\" },\n info: { icon: UiInfo, label: \"Info\", className: \"text-sky-600 dark:text-sky-400\" },\n};\n\nexport type ToolKind = \"test\" | \"lint\" | \"fixture\" | \"runner\";\n\nexport type ToolMark = {\n icon: StaticIconComponent;\n label: string;\n kind: ToolKind;\n className: string;\n};\n\nconst GO = \"text-[#00add8]\";\nconst MARKDOWN = \"text-foreground\";\nconst MUTED = \"text-muted-foreground\";\n\n/**\n * Keyed by the identifiers gavel reports: testrunner/parsers Framework values\n * and each linter's Name(). A tool without a mark of its own takes its\n * language's (golangci-lint → Go, pyright → Python).\n */\nexport const TEST_TOOLS: Record<string, ToolMark> = {\n \"go test\": { icon: UiLogoGo, label: \"go test\", kind: \"test\", className: GO },\n ginkgo: { icon: UiLogoGo, label: \"Ginkgo\", kind: \"test\", className: GO },\n vitest: { icon: UiLogoVitest, label: \"Vitest\", kind: \"test\", className: \"text-[#6e9f18]\" },\n jest: { icon: UiLogoJest, label: \"Jest\", kind: \"test\", className: \"text-[#c21325] dark:text-[#e5576a]\" },\n playwright: {\n icon: UiLogoPlaywright,\n label: \"Playwright\",\n kind: \"test\",\n className: \"text-[#2ead33]\",\n },\n fixture: { icon: UiLogoMarkdown, label: \"Markdown fixture\", kind: \"test\", className: MARKDOWN },\n task: { icon: UiTerminal, label: \"Task\", kind: \"runner\", className: MUTED },\n // Fixture step types (gavel fixtures FixtureResult.Type) and the\n // \"checklist\" node verifyReportTests synthesizes for acceptance criteria.\n exec: { icon: UiTerminal, label: \"Command\", kind: \"fixture\", className: MUTED },\n test: { icon: UiBeaker, label: \"Test step\", kind: \"fixture\", className: MUTED },\n lint: { icon: UiListChecks, label: \"Lint step\", kind: \"fixture\", className: MUTED },\n verify: {\n icon: UiSparkles,\n label: \"AI review\",\n kind: \"fixture\",\n className: \"text-violet-600 dark:text-violet-400\",\n },\n query: { icon: UiDatabase, label: \"Query\", kind: \"fixture\", className: MUTED },\n criterion: { icon: UiSealCheck, label: \"Acceptance criterion\", kind: \"fixture\", className: MUTED },\n checklist: { icon: UiSealCheck, label: \"Acceptance criteria\", kind: \"fixture\", className: MUTED },\n \"golangci-lint\": { icon: UiLogoGo, label: \"golangci-lint\", kind: \"lint\", className: GO },\n eslint: {\n icon: UiLogoEslint,\n label: \"ESLint\",\n kind: \"lint\",\n className: \"text-[#4b32c3] dark:text-[#8080f2]\",\n },\n oxlint: {\n icon: UiLogoOxc,\n label: \"oxlint\",\n kind: \"lint\",\n className: \"text-[#0891b2] dark:text-[#00f7f1]\",\n },\n tsc: {\n icon: UiLogoTypescript,\n label: \"TypeScript compiler\",\n kind: \"lint\",\n className: \"text-[#3178c6]\",\n },\n markdownlint: { icon: UiLogoMarkdown, label: \"markdownlint\", kind: \"lint\", className: MARKDOWN },\n vale: { icon: UiLogoMarkdown, label: \"Vale (prose)\", kind: \"lint\", className: MARKDOWN },\n pyright: {\n icon: UiLogoPython,\n label: \"Pyright\",\n kind: \"lint\",\n className: \"text-[#3776ab] dark:text-[#ffd43b]\",\n },\n ruff: {\n icon: UiLogoRuff,\n label: \"Ruff\",\n kind: \"lint\",\n className: \"text-[#261230] dark:text-[#d7ff64]\",\n },\n // A secret scanner's finding is about a credential, not a language.\n betterleaks: { icon: UiKey, label: \"betterleaks (secrets)\", kind: \"lint\", className: MUTED },\n jscpd: { icon: UiCopy, label: \"jscpd (duplicate code)\", kind: \"lint\", className: MUTED },\n reactdoctor: {\n icon: UiLogoReact,\n label: \"React Doctor\",\n kind: \"lint\",\n className: \"text-[#0e7490] dark:text-[#61dafb]\",\n },\n};\n\n/** The tool's mark, or null for a tool the catalog doesn't know (render its name alone). */\nexport function toolMark(tool: string | undefined): ToolMark | null {\n return (tool && TEST_TOOLS[tool]) || null;\n}\n\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"marks.js","sources":["../../../src/data/test-runner/marks.ts"],"sourcesContent":["// The shared iconography for test and lint results: what happened (outcome),\n// how bad a finding is (lint severity), and what produced it (tool).\n//\n// Every outcome has its own outline glyph, so a result never reads by colour\n// alone.\n// Tool marks are bundled Simple Icons brand glyphs (offline, no runtime icon\n// names), coloured per theme here because one brand hex can't hold up on both\n// light and dark surfaces.\n\nimport type { StaticIconComponent } from \"../Icon\";\nimport {\n UiCircleOutline,\n UiCircleX,\n UiBeaker,\n UiChatDots,\n UiClipboardText,\n UiCopy,\n UiCursorClick,\n UiDatabase,\n UiGlobe,\n UiInfo,\n UiKey,\n UiListChecks,\n UiLoader,\n UiLogoEslint,\n UiLogoGo,\n UiLogoJest,\n UiLogoMarkdown,\n UiLogoOxc,\n UiLogoPlaywright,\n UiLogoPython,\n UiLogoReact,\n UiLogoRuff,\n UiLogoTypescript,\n UiLogoVitest,\n UiPass,\n UiProhibit,\n UiRobotAi,\n UiSealCheck,\n UiServer,\n UiSkipForward,\n UiSparkles,\n UiTerminal,\n UiTimer,\n UiWarningTriangle,\n} from \"../../icons\";\nimport { STATUS_LABELS, type TestOutcome } from \"./status\";\n\nexport type { TestOutcome } from \"./status\";\n\nexport type ResultMark = {\n icon: StaticIconComponent;\n label: string;\n /** Text colour classes for light and dark themes. */\n className: string;\n spin?: boolean;\n};\n\nconst LABEL = Object.fromEntries(STATUS_LABELS.map((s) => [s.key, s.label]));\n\nconst RED = \"text-red-600 dark:text-red-400\";\nconst AMBER = \"text-amber-600 dark:text-amber-400\";\n\nexport const TEST_OUTCOMES: Record<TestOutcome, ResultMark> = {\n failed: { icon: UiCircleX, label: LABEL.failed!, className: RED },\n errored: { icon: UiProhibit, label: \"Could not run\", className: RED },\n timedout: { icon: UiTimer, label: LABEL.timedout!, className: AMBER },\n warned: { icon: UiWarningTriangle, label: LABEL.warned!, className: AMBER },\n passed: {\n icon: UiPass,\n label: LABEL.passed!,\n className: \"text-emerald-600 dark:text-emerald-400\",\n },\n skipped: { icon: UiSkipForward, label: LABEL.skipped!, className: \"text-slate-500\" },\n pending: {\n icon: UiCircleOutline,\n label: LABEL.pending!,\n className: \"text-slate-400 dark:text-slate-500\",\n },\n running: {\n icon: UiLoader,\n label: LABEL.running!,\n className: \"text-blue-600 dark:text-blue-400\",\n spin: true,\n },\n};\n\nexport type LintSeverity = \"error\" | \"warning\" | \"info\";\n\n/** An error-severity finding fails the verdict and a warning warns it, so they share those outcomes' marks. */\nexport const LINT_SEVERITIES: Record<LintSeverity, ResultMark> = {\n error: { ...TEST_OUTCOMES.failed, label: \"Error\" },\n warning: { ...TEST_OUTCOMES.warned, label: \"Warning\" },\n info: { icon: UiInfo, label: \"Info\", className: \"text-sky-600 dark:text-sky-400\" },\n};\n\nexport type ToolKind = \"test\" | \"lint\" | \"fixture\" | \"runner\";\n\nexport type ToolMark = {\n icon: StaticIconComponent;\n label: string;\n kind: ToolKind;\n className: string;\n};\n\nconst GO = \"text-[#00add8]\";\nconst MARKDOWN = \"text-foreground\";\nconst MUTED = \"text-muted-foreground\";\n\n/**\n * Keyed by the identifiers gavel reports: testrunner/parsers Framework values\n * and each linter's Name(). A tool without a mark of its own takes its\n * language's (golangci-lint → Go, pyright → Python).\n */\nexport const TEST_TOOLS: Record<string, ToolMark> = {\n \"go test\": { icon: UiLogoGo, label: \"go test\", kind: \"test\", className: GO },\n ginkgo: { icon: UiLogoGo, label: \"Ginkgo\", kind: \"test\", className: GO },\n vitest: { icon: UiLogoVitest, label: \"Vitest\", kind: \"test\", className: \"text-[#6e9f18]\" },\n jest: { icon: UiLogoJest, label: \"Jest\", kind: \"test\", className: \"text-[#c21325] dark:text-[#e5576a]\" },\n playwright: {\n icon: UiLogoPlaywright,\n label: \"Playwright\",\n kind: \"test\",\n className: \"text-[#2ead33]\",\n },\n fixture: { icon: UiLogoMarkdown, label: \"Markdown fixture\", kind: \"test\", className: MARKDOWN },\n task: { icon: UiTerminal, label: \"Task\", kind: \"runner\", className: MUTED },\n // Fixture step types (gavel fixtures FixtureResult.Type) and the\n // \"checklist\" node verifyReportTests synthesizes for acceptance criteria.\n exec: { icon: UiTerminal, label: \"Command\", kind: \"fixture\", className: MUTED },\n test: { icon: UiBeaker, label: \"Test step\", kind: \"fixture\", className: MUTED },\n lint: { icon: UiListChecks, label: \"Lint step\", kind: \"fixture\", className: MUTED },\n verify: {\n icon: UiSparkles,\n label: \"AI review\",\n kind: \"fixture\",\n className: \"text-violet-600 dark:text-violet-400\",\n },\n query: { icon: UiDatabase, label: \"Query\", kind: \"fixture\", className: MUTED },\n // Typed step kinds (gavel fixtures/kind registry): the node's `framework`\n // is the kind name. `ai` is the runner behind `verify` but is a prompt/CEL\n // round-trip rather than an acceptance-criteria review, so it takes the\n // same violet family with its own glyph and label.\n services: { icon: UiServer, label: \"Services\", kind: \"fixture\", className: MUTED },\n todo: { icon: UiClipboardText, label: \"TODO step\", kind: \"fixture\", className: MUTED },\n todo_prompt: { icon: UiChatDots, label: \"TODO prompt run\", kind: \"fixture\", className: MUTED },\n ai: {\n icon: UiRobotAi,\n label: \"AI step\",\n kind: \"fixture\",\n className: \"text-violet-600 dark:text-violet-400\",\n },\n browser: { icon: UiGlobe, label: \"Browser\", kind: \"fixture\", className: MUTED },\n // The fixture-step twin of the `playwright` test framework above; both\n // arrive as framework \"playwright\", so toolKeyFor() picks this key when the\n // node carries `detail.step`. Muted like every other fixture step, so the\n // brand green stays on the spec results the framework reports.\n \"playwright-step\": {\n icon: UiLogoPlaywright,\n label: \"Playwright step\",\n kind: \"fixture\",\n className: MUTED,\n },\n \"agent-browser\": { icon: UiCursorClick, label: \"Agent browser\", kind: \"fixture\", className: MUTED },\n criterion: { icon: UiSealCheck, label: \"Acceptance criterion\", kind: \"fixture\", className: MUTED },\n checklist: { icon: UiSealCheck, label: \"Acceptance criteria\", kind: \"fixture\", className: MUTED },\n \"golangci-lint\": { icon: UiLogoGo, label: \"golangci-lint\", kind: \"lint\", className: GO },\n eslint: {\n icon: UiLogoEslint,\n label: \"ESLint\",\n kind: \"lint\",\n className: \"text-[#4b32c3] dark:text-[#8080f2]\",\n },\n oxlint: {\n icon: UiLogoOxc,\n label: \"oxlint\",\n kind: \"lint\",\n className: \"text-[#0891b2] dark:text-[#00f7f1]\",\n },\n tsc: {\n icon: UiLogoTypescript,\n label: \"TypeScript compiler\",\n kind: \"lint\",\n className: \"text-[#3178c6]\",\n },\n markdownlint: { icon: UiLogoMarkdown, label: \"markdownlint\", kind: \"lint\", className: MARKDOWN },\n vale: { icon: UiLogoMarkdown, label: \"Vale (prose)\", kind: \"lint\", className: MARKDOWN },\n pyright: {\n icon: UiLogoPython,\n label: \"Pyright\",\n kind: \"lint\",\n className: \"text-[#3776ab] dark:text-[#ffd43b]\",\n },\n ruff: {\n icon: UiLogoRuff,\n label: \"Ruff\",\n kind: \"lint\",\n className: \"text-[#261230] dark:text-[#d7ff64]\",\n },\n // A secret scanner's finding is about a credential, not a language.\n betterleaks: { icon: UiKey, label: \"betterleaks (secrets)\", kind: \"lint\", className: MUTED },\n jscpd: { icon: UiCopy, label: \"jscpd (duplicate code)\", kind: \"lint\", className: MUTED },\n reactdoctor: {\n icon: UiLogoReact,\n label: \"React Doctor\",\n kind: \"lint\",\n className: \"text-[#0e7490] dark:text-[#61dafb]\",\n },\n};\n\n/** The tool's mark, or null for a tool the catalog doesn't know (render its name alone). */\nexport function toolMark(tool: string | undefined): ToolMark | null {\n return (tool && TEST_TOOLS[tool]) || null;\n}\n\nfunction hasStepInfo(detail: unknown): boolean {\n if (detail === null || typeof detail !== \"object\" || Array.isArray(detail)) return false;\n const step = (detail as Record<string, unknown>).step;\n return step !== null && typeof step === \"object\" && !Array.isArray(step);\n}\n\n/**\n * The TEST_TOOLS key for a node. A node's `framework` is the key, except that\n * \"playwright\" names both the test framework and the `playwright` fixture-step\n * kind: a node carrying `detail.step` (the fixture verifier's step id and\n * kind) is the step.\n */\nexport function toolKeyFor(node: { framework?: string | undefined; detail?: unknown }): string | undefined {\n if (node.framework === \"playwright\" && hasStepInfo(node.detail)) return \"playwright-step\";\n return node.framework;\n}\n\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,93 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const jsxRuntime = require("react/jsx-runtime");
4
+ const react = require("react");
5
+ const format = require("../../lib/format.cjs");
6
+ const utils$1 = require("../../lib/utils.cjs");
7
+ const utils = require("../diagnostics/utils.cjs");
8
+ const Icon = require("../Icon.cjs");
9
+ const Tree = require("../Tree.cjs");
10
+ const processTree = require("./process-tree.cjs");
11
+ const Sparkline = require("./Sparkline.cjs");
12
+ const UiServerProcess = require("../../icons/components/UiServerProcess.cjs");
13
+ const UiDebug = require("../../icons/components/UiDebug.cjs");
14
+ const OPEN_DEPTH = 2;
15
+ function lifetimeMs(row) {
16
+ const end = row.exitedAt ?? row.lastSeenAt;
17
+ return Date.parse(end) - Date.parse(row.startedAt);
18
+ }
19
+ function ProcessRow({ node, selected, series }) {
20
+ const { row, exited } = node;
21
+ return /* @__PURE__ */ jsxRuntime.jsxs(
22
+ "div",
23
+ {
24
+ "data-process-key": row.key,
25
+ ...exited ? { "data-exited": "true" } : {},
26
+ className: utils$1.cn("flex min-w-0 flex-1 items-center gap-1.5", exited && "opacity-60"),
27
+ title: row.command,
28
+ children: [
29
+ /* @__PURE__ */ jsxRuntime.jsx(
30
+ Icon.Icon,
31
+ {
32
+ icon: row.isRoot ? UiServerProcess.UiServerProcess : UiDebug.UiDebug,
33
+ className: row.isRoot ? "text-base text-blue-600" : "text-base text-muted-foreground"
34
+ }
35
+ ),
36
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: utils$1.cn("truncate", selected ? "font-semibold text-primary" : "font-medium text-foreground"), children: utils.processLabel({ pid: row.pid, name: row.name, command: row.command }) }),
37
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "shrink-0 text-xs text-muted-foreground", children: [
38
+ "pid ",
39
+ row.pid
40
+ ] }),
41
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1" }),
42
+ series ? /* @__PURE__ */ jsxRuntime.jsx(Sparkline.Sparkline, { values: series, label: `CPU % over ${series.length} samples` }) : null,
43
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: utils$1.cn("inline-flex shrink-0 items-center gap-1 text-xs", utils.processStateColor(row.status)), children: [
44
+ /* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon: utils.processStateIcon(row.status) }),
45
+ row.status
46
+ ] }),
47
+ exited ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
48
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 text-xs tabular-nums text-muted-foreground", title: "Lifetime", children: format.formatDuration(lifetimeMs(row)) }),
49
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 text-xs tabular-nums text-muted-foreground", title: "Peak CPU", children: `peak ${row.peakCpuPercent.toFixed(1)}%` }),
50
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 text-xs tabular-nums text-muted-foreground", title: "Peak RSS", children: `peak ${format.formatBytes(row.peakRssBytes)}` })
51
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
52
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 text-xs tabular-nums text-muted-foreground", title: `Peak ${row.peakCpuPercent.toFixed(1)}%`, children: `${row.cpuPercent.toFixed(1)}%` }),
53
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 text-xs tabular-nums text-muted-foreground", title: `Peak ${format.formatBytes(row.peakRssBytes)}`, children: format.formatBytes(row.rssBytes) })
54
+ ] })
55
+ ]
56
+ }
57
+ );
58
+ }
59
+ function ProcessTraceTree({
60
+ nodes,
61
+ samples,
62
+ onSelect,
63
+ selectedKey,
64
+ emptyLabel = "No processes recorded",
65
+ className
66
+ }) {
67
+ const roots = react.useMemo(() => processTree.buildProcessForest(nodes), [nodes]);
68
+ const series = react.useMemo(() => processTree.cpuSeriesByKey(samples ?? []), [samples]);
69
+ const selected = react.useMemo(
70
+ () => selectedKey ? processTree.flattenForest(roots).find((node) => node.row.key === selectedKey) ?? null : null,
71
+ [roots, selectedKey]
72
+ );
73
+ return /* @__PURE__ */ jsxRuntime.jsx(
74
+ Tree.Tree,
75
+ {
76
+ roots,
77
+ ...className ? { className } : {},
78
+ ariaLabel: "Process tree",
79
+ empty: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-density-6 text-center text-muted-foreground text-sm", children: emptyLabel }),
80
+ getChildren: (node) => node.children,
81
+ getKey: (node) => node.row.key,
82
+ getSearchText: (node) => `${node.row.name} ${node.row.command} ${node.row.pid}`,
83
+ selected,
84
+ revealSelected: true,
85
+ defaultOpen: (_node, depth) => depth < OPEN_DEPTH,
86
+ onSelect: (node) => onSelect(node.row.key),
87
+ renderRow: ({ node, selected: isSelected }) => /* @__PURE__ */ jsxRuntime.jsx(ProcessRow, { node, selected: isSelected, series: series.get(node.row.key) }),
88
+ rowClass: (_node, isSelected) => isSelected ? "bg-primary/10 border-l-2 border-primary" : "hover:bg-accent"
89
+ }
90
+ );
91
+ }
92
+ exports.ProcessTraceTree = ProcessTraceTree;
93
+ //# sourceMappingURL=ProcessTraceTree.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProcessTraceTree.cjs","sources":["../../../src/data/traces/ProcessTraceTree.tsx"],"sourcesContent":["import { useMemo } from \"react\";\nimport { UiDebug, UiServerProcess } from \"../../icons\";\nimport { formatBytes, formatDuration } from \"../../lib/format\";\nimport { cn } from \"../../lib/utils\";\nimport { processLabel, processStateColor, processStateIcon } from \"../diagnostics/utils\";\nimport { Icon } from \"../Icon\";\nimport { Tree } from \"../Tree\";\nimport { buildProcessForest, cpuSeriesByKey, flattenForest, type ProcessTreeNode } from \"./process-tree\";\nimport { Sparkline } from \"./Sparkline\";\nimport type { ProcessNodeRow, ProcessSampleRow } from \"./types\";\n\n/** Roots and their direct children start open; deeper levels open on demand or to reveal the selection. */\nconst OPEN_DEPTH = 2;\n\nexport type ProcessTraceTreeProps = {\n /** `process_tree` trace records, one per process. */\n nodes: ProcessNodeRow[];\n /** `process_samples` records; a process with at least two shows a CPU sparkline. */\n samples?: ProcessSampleRow[];\n /** Called with the `key` of the clicked process. */\n onSelect: (key: string) => void;\n /** `key` of the selected process. */\n selectedKey?: string | null;\n /** Shown when there are no processes. */\n emptyLabel?: string;\n /** Classes applied to the tree root. */\n className?: string;\n};\n\nfunction lifetimeMs(row: ProcessNodeRow): number {\n const end = row.exitedAt ?? row.lastSeenAt;\n return Date.parse(end) - Date.parse(row.startedAt);\n}\n\nfunction ProcessRow({ node, selected, series }: { node: ProcessTreeNode; selected: boolean; series: number[] | undefined }) {\n const { row, exited } = node;\n return (\n <div\n data-process-key={row.key}\n {...(exited ? { \"data-exited\": \"true\" } : {})}\n className={cn(\"flex min-w-0 flex-1 items-center gap-1.5\", exited && \"opacity-60\")}\n title={row.command}\n >\n <Icon\n icon={row.isRoot ? UiServerProcess : UiDebug}\n className={row.isRoot ? \"text-base text-blue-600\" : \"text-base text-muted-foreground\"}\n />\n <span className={cn(\"truncate\", selected ? \"font-semibold text-primary\" : \"font-medium text-foreground\")}>\n {processLabel({ pid: row.pid, name: row.name, command: row.command })}\n </span>\n <span className=\"shrink-0 text-xs text-muted-foreground\">pid {row.pid}</span>\n <span className=\"flex-1\" />\n {series ? <Sparkline values={series} label={`CPU % over ${series.length} samples`} /> : null}\n <span className={cn(\"inline-flex shrink-0 items-center gap-1 text-xs\", processStateColor(row.status))}>\n <Icon icon={processStateIcon(row.status)} />\n {row.status}\n </span>\n {exited ? (\n <>\n <span className=\"shrink-0 text-xs tabular-nums text-muted-foreground\" title=\"Lifetime\">\n {formatDuration(lifetimeMs(row))}\n </span>\n <span className=\"shrink-0 text-xs tabular-nums text-muted-foreground\" title=\"Peak CPU\">\n {`peak ${row.peakCpuPercent.toFixed(1)}%`}\n </span>\n <span className=\"shrink-0 text-xs tabular-nums text-muted-foreground\" title=\"Peak RSS\">\n {`peak ${formatBytes(row.peakRssBytes)}`}\n </span>\n </>\n ) : (\n <>\n <span className=\"shrink-0 text-xs tabular-nums text-muted-foreground\" title={`Peak ${row.peakCpuPercent.toFixed(1)}%`}>\n {`${row.cpuPercent.toFixed(1)}%`}\n </span>\n <span className=\"shrink-0 text-xs tabular-nums text-muted-foreground\" title={`Peak ${formatBytes(row.peakRssBytes)}`}>\n {formatBytes(row.rssBytes)}\n </span>\n </>\n )}\n </div>\n );\n}\n\n/**\n * The process tree of a `process_tree` trace: a forest keyed by `key` /\n * `parentKey`, where a process whose parent was never recorded is a root.\n * Exited processes are dimmed and show their lifetime and peak usage. It is\n * built on the same `Tree` primitive and process helpers as `DiagnosticsTree`,\n * which cannot take a forest, key selection or exit information.\n */\nexport function ProcessTraceTree({\n nodes,\n samples,\n onSelect,\n selectedKey,\n emptyLabel = \"No processes recorded\",\n className,\n}: ProcessTraceTreeProps) {\n const roots = useMemo(() => buildProcessForest(nodes), [nodes]);\n const series = useMemo(() => cpuSeriesByKey(samples ?? []), [samples]);\n const selected = useMemo(\n () => (selectedKey ? (flattenForest(roots).find((node) => node.row.key === selectedKey) ?? null) : null),\n [roots, selectedKey],\n );\n\n return (\n <Tree<ProcessTreeNode>\n roots={roots}\n {...(className ? { className } : {})}\n ariaLabel=\"Process tree\"\n empty={<div className=\"p-density-6 text-center text-muted-foreground text-sm\">{emptyLabel}</div>}\n getChildren={(node) => node.children}\n getKey={(node) => node.row.key}\n getSearchText={(node) => `${node.row.name} ${node.row.command} ${node.row.pid}`}\n selected={selected}\n revealSelected\n defaultOpen={(_node, depth) => depth < OPEN_DEPTH}\n onSelect={(node) => onSelect(node.row.key)}\n renderRow={({ node, selected: isSelected }) => (\n <ProcessRow node={node} selected={isSelected} series={series.get(node.row.key)} />\n )}\n rowClass={(_node, isSelected) =>\n isSelected ? \"bg-primary/10 border-l-2 border-primary\" : \"hover:bg-accent\"\n }\n />\n );\n}\n"],"names":["jsxs","cn","jsx","Icon","UiServerProcess","UiDebug","processLabel","Sparkline","processStateColor","processStateIcon","Fragment","formatDuration","formatBytes","useMemo","buildProcessForest","cpuSeriesByKey","flattenForest","Tree"],"mappings":";;;;;;;;;;;;;AAYA,MAAM,aAAa;AAiBnB,SAAS,WAAW,KAA6B;AAC/C,QAAM,MAAM,IAAI,YAAY,IAAI;AAChC,SAAO,KAAK,MAAM,GAAG,IAAI,KAAK,MAAM,IAAI,SAAS;AACnD;AAEA,SAAS,WAAW,EAAE,MAAM,UAAU,UAAsF;AAC1H,QAAM,EAAE,KAAK,OAAA,IAAW;AACxB,SACEA,2BAAAA;AAAAA,IAAC;AAAA,IAAA;AAAA,MACC,oBAAkB,IAAI;AAAA,MACrB,GAAI,SAAS,EAAE,eAAe,OAAA,IAAW,CAAA;AAAA,MAC1C,WAAWC,QAAAA,GAAG,4CAA4C,UAAU,YAAY;AAAA,MAChF,OAAO,IAAI;AAAA,MAEX,UAAA;AAAA,QAAAC,2BAAAA;AAAAA,UAACC,KAAAA;AAAAA,UAAA;AAAA,YACC,MAAM,IAAI,SAASC,gBAAAA,kBAAkBC,QAAAA;AAAAA,YACrC,WAAW,IAAI,SAAS,4BAA4B;AAAA,UAAA;AAAA,QAAA;AAAA,QAEtDH,+BAAC,UAAK,WAAWD,QAAAA,GAAG,YAAY,WAAW,+BAA+B,6BAA6B,GACpG,UAAAK,MAAAA,aAAa,EAAE,KAAK,IAAI,KAAK,MAAM,IAAI,MAAM,SAAS,IAAI,QAAA,CAAS,GACtE;AAAA,QACAN,2BAAAA,KAAC,QAAA,EAAK,WAAU,0CAAyC,UAAA;AAAA,UAAA;AAAA,UAAK,IAAI;AAAA,QAAA,GAAI;AAAA,QACtEE,2BAAAA,IAAC,QAAA,EAAK,WAAU,SAAA,CAAS;AAAA,QACxB,SAASA,2BAAAA,IAACK,UAAAA,WAAA,EAAU,QAAQ,QAAQ,OAAO,cAAc,OAAO,MAAM,WAAA,CAAY,IAAK;AAAA,QACxFP,2BAAAA,KAAC,UAAK,WAAWC,WAAG,mDAAmDO,MAAAA,kBAAkB,IAAI,MAAM,CAAC,GAClG,UAAA;AAAA,UAAAN,2BAAAA,IAACC,KAAAA,MAAA,EAAK,MAAMM,MAAAA,iBAAiB,IAAI,MAAM,GAAG;AAAA,UACzC,IAAI;AAAA,QAAA,GACP;AAAA,QACC,SACCT,2BAAAA,KAAAU,qBAAA,EACE,UAAA;AAAA,UAAAR,2BAAAA,IAAC,QAAA,EAAK,WAAU,uDAAsD,OAAM,YACzE,UAAAS,OAAAA,eAAe,WAAW,GAAG,CAAC,EAAA,CACjC;AAAA,UACAT,2BAAAA,IAAC,QAAA,EAAK,WAAU,uDAAsD,OAAM,YACzE,UAAA,QAAQ,IAAI,eAAe,QAAQ,CAAC,CAAC,KACxC;AAAA,UACAA,2BAAAA,IAAC,QAAA,EAAK,WAAU,uDAAsD,OAAM,YACzE,UAAA,QAAQU,OAAAA,YAAY,IAAI,YAAY,CAAC,GAAA,CACxC;AAAA,QAAA,EAAA,CACF,IAEAZ,2BAAAA,KAAAU,WAAAA,UAAA,EACE,UAAA;AAAA,UAAAR,+BAAC,UAAK,WAAU,uDAAsD,OAAO,QAAQ,IAAI,eAAe,QAAQ,CAAC,CAAC,KAC/G,UAAA,GAAG,IAAI,WAAW,QAAQ,CAAC,CAAC,KAC/B;AAAA,UACAA,2BAAAA,IAAC,QAAA,EAAK,WAAU,uDAAsD,OAAO,QAAQU,OAAAA,YAAY,IAAI,YAAY,CAAC,IAC/G,UAAAA,OAAAA,YAAY,IAAI,QAAQ,EAAA,CAC3B;AAAA,QAAA,EAAA,CACF;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAIR;AASO,SAAS,iBAAiB;AAAA,EAC/B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,aAAa;AAAA,EACb;AACF,GAA0B;AACxB,QAAM,QAAQC,MAAAA,QAAQ,MAAMC,YAAAA,mBAAmB,KAAK,GAAG,CAAC,KAAK,CAAC;AAC9D,QAAM,SAASD,MAAAA,QAAQ,MAAME,YAAAA,eAAe,WAAW,EAAE,GAAG,CAAC,OAAO,CAAC;AACrE,QAAM,WAAWF,MAAAA;AAAAA,IACf,MAAO,cAAeG,YAAAA,cAAc,KAAK,EAAE,KAAK,CAAC,SAAS,KAAK,IAAI,QAAQ,WAAW,KAAK,OAAQ;AAAA,IACnG,CAAC,OAAO,WAAW;AAAA,EAAA;AAGrB,SACEd,2BAAAA;AAAAA,IAACe,KAAAA;AAAAA,IAAA;AAAA,MACC;AAAA,MACC,GAAI,YAAY,EAAE,UAAA,IAAc,CAAA;AAAA,MACjC,WAAU;AAAA,MACV,OAAOf,2BAAAA,IAAC,OAAA,EAAI,WAAU,yDAAyD,UAAA,YAAW;AAAA,MAC1F,aAAa,CAAC,SAAS,KAAK;AAAA,MAC5B,QAAQ,CAAC,SAAS,KAAK,IAAI;AAAA,MAC3B,eAAe,CAAC,SAAS,GAAG,KAAK,IAAI,IAAI,IAAI,KAAK,IAAI,OAAO,IAAI,KAAK,IAAI,GAAG;AAAA,MAC7E;AAAA,MACA,gBAAc;AAAA,MACd,aAAa,CAAC,OAAO,UAAU,QAAQ;AAAA,MACvC,UAAU,CAAC,SAAS,SAAS,KAAK,IAAI,GAAG;AAAA,MACzC,WAAW,CAAC,EAAE,MAAM,UAAU,iBAC5BA,2BAAAA,IAAC,YAAA,EAAW,MAAY,UAAU,YAAY,QAAQ,OAAO,IAAI,KAAK,IAAI,GAAG,GAAG;AAAA,MAElF,UAAU,CAAC,OAAO,eAChB,aAAa,4CAA4C;AAAA,IAAA;AAAA,EAAA;AAIjE;;"}
@@ -0,0 +1,24 @@
1
+ import { ProcessNodeRow, ProcessSampleRow } from './types.js';
2
+ export type ProcessTraceTreeProps = {
3
+ /** `process_tree` trace records, one per process. */
4
+ nodes: ProcessNodeRow[];
5
+ /** `process_samples` records; a process with at least two shows a CPU sparkline. */
6
+ samples?: ProcessSampleRow[];
7
+ /** Called with the `key` of the clicked process. */
8
+ onSelect: (key: string) => void;
9
+ /** `key` of the selected process. */
10
+ selectedKey?: string | null;
11
+ /** Shown when there are no processes. */
12
+ emptyLabel?: string;
13
+ /** Classes applied to the tree root. */
14
+ className?: string;
15
+ };
16
+ /**
17
+ * The process tree of a `process_tree` trace: a forest keyed by `key` /
18
+ * `parentKey`, where a process whose parent was never recorded is a root.
19
+ * Exited processes are dimmed and show their lifetime and peak usage. It is
20
+ * built on the same `Tree` primitive and process helpers as `DiagnosticsTree`,
21
+ * which cannot take a forest, key selection or exit information.
22
+ */
23
+ export declare function ProcessTraceTree({ nodes, samples, onSelect, selectedKey, emptyLabel, className, }: ProcessTraceTreeProps): import("react").JSX.Element;
24
+ //# sourceMappingURL=ProcessTraceTree.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProcessTraceTree.d.ts","sourceRoot":"","sources":["../../../src/data/traces/ProcessTraceTree.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAKhE,MAAM,MAAM,qBAAqB,GAAG;IAClC,qDAAqD;IACrD,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,oFAAoF;IACpF,OAAO,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAC7B,oDAAoD;IACpD,QAAQ,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,qCAAqC;IACrC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,yCAAyC;IACzC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,wCAAwC;IACxC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAwDF;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,KAAK,EACL,OAAO,EACP,QAAQ,EACR,WAAW,EACX,UAAoC,EACpC,SAAS,GACV,EAAE,qBAAqB,+BA6BvB"}
@@ -0,0 +1,93 @@
1
+ import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
+ import { useMemo } from "react";
3
+ import { formatDuration, formatBytes } from "../../lib/format.js";
4
+ import { cn } from "../../lib/utils.js";
5
+ import { processLabel, processStateIcon, processStateColor } from "../diagnostics/utils.js";
6
+ import { Icon } from "../Icon.js";
7
+ import { Tree } from "../Tree.js";
8
+ import { buildProcessForest, cpuSeriesByKey, flattenForest } from "./process-tree.js";
9
+ import { Sparkline } from "./Sparkline.js";
10
+ import { UiServerProcess } from "../../icons/components/UiServerProcess.js";
11
+ import { UiDebug } from "../../icons/components/UiDebug.js";
12
+ const OPEN_DEPTH = 2;
13
+ function lifetimeMs(row) {
14
+ const end = row.exitedAt ?? row.lastSeenAt;
15
+ return Date.parse(end) - Date.parse(row.startedAt);
16
+ }
17
+ function ProcessRow({ node, selected, series }) {
18
+ const { row, exited } = node;
19
+ return /* @__PURE__ */ jsxs(
20
+ "div",
21
+ {
22
+ "data-process-key": row.key,
23
+ ...exited ? { "data-exited": "true" } : {},
24
+ className: cn("flex min-w-0 flex-1 items-center gap-1.5", exited && "opacity-60"),
25
+ title: row.command,
26
+ children: [
27
+ /* @__PURE__ */ jsx(
28
+ Icon,
29
+ {
30
+ icon: row.isRoot ? UiServerProcess : UiDebug,
31
+ className: row.isRoot ? "text-base text-blue-600" : "text-base text-muted-foreground"
32
+ }
33
+ ),
34
+ /* @__PURE__ */ jsx("span", { className: cn("truncate", selected ? "font-semibold text-primary" : "font-medium text-foreground"), children: processLabel({ pid: row.pid, name: row.name, command: row.command }) }),
35
+ /* @__PURE__ */ jsxs("span", { className: "shrink-0 text-xs text-muted-foreground", children: [
36
+ "pid ",
37
+ row.pid
38
+ ] }),
39
+ /* @__PURE__ */ jsx("span", { className: "flex-1" }),
40
+ series ? /* @__PURE__ */ jsx(Sparkline, { values: series, label: `CPU % over ${series.length} samples` }) : null,
41
+ /* @__PURE__ */ jsxs("span", { className: cn("inline-flex shrink-0 items-center gap-1 text-xs", processStateColor(row.status)), children: [
42
+ /* @__PURE__ */ jsx(Icon, { icon: processStateIcon(row.status) }),
43
+ row.status
44
+ ] }),
45
+ exited ? /* @__PURE__ */ jsxs(Fragment, { children: [
46
+ /* @__PURE__ */ jsx("span", { className: "shrink-0 text-xs tabular-nums text-muted-foreground", title: "Lifetime", children: formatDuration(lifetimeMs(row)) }),
47
+ /* @__PURE__ */ jsx("span", { className: "shrink-0 text-xs tabular-nums text-muted-foreground", title: "Peak CPU", children: `peak ${row.peakCpuPercent.toFixed(1)}%` }),
48
+ /* @__PURE__ */ jsx("span", { className: "shrink-0 text-xs tabular-nums text-muted-foreground", title: "Peak RSS", children: `peak ${formatBytes(row.peakRssBytes)}` })
49
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
50
+ /* @__PURE__ */ jsx("span", { className: "shrink-0 text-xs tabular-nums text-muted-foreground", title: `Peak ${row.peakCpuPercent.toFixed(1)}%`, children: `${row.cpuPercent.toFixed(1)}%` }),
51
+ /* @__PURE__ */ jsx("span", { className: "shrink-0 text-xs tabular-nums text-muted-foreground", title: `Peak ${formatBytes(row.peakRssBytes)}`, children: formatBytes(row.rssBytes) })
52
+ ] })
53
+ ]
54
+ }
55
+ );
56
+ }
57
+ function ProcessTraceTree({
58
+ nodes,
59
+ samples,
60
+ onSelect,
61
+ selectedKey,
62
+ emptyLabel = "No processes recorded",
63
+ className
64
+ }) {
65
+ const roots = useMemo(() => buildProcessForest(nodes), [nodes]);
66
+ const series = useMemo(() => cpuSeriesByKey(samples ?? []), [samples]);
67
+ const selected = useMemo(
68
+ () => selectedKey ? flattenForest(roots).find((node) => node.row.key === selectedKey) ?? null : null,
69
+ [roots, selectedKey]
70
+ );
71
+ return /* @__PURE__ */ jsx(
72
+ Tree,
73
+ {
74
+ roots,
75
+ ...className ? { className } : {},
76
+ ariaLabel: "Process tree",
77
+ empty: /* @__PURE__ */ jsx("div", { className: "p-density-6 text-center text-muted-foreground text-sm", children: emptyLabel }),
78
+ getChildren: (node) => node.children,
79
+ getKey: (node) => node.row.key,
80
+ getSearchText: (node) => `${node.row.name} ${node.row.command} ${node.row.pid}`,
81
+ selected,
82
+ revealSelected: true,
83
+ defaultOpen: (_node, depth) => depth < OPEN_DEPTH,
84
+ onSelect: (node) => onSelect(node.row.key),
85
+ renderRow: ({ node, selected: isSelected }) => /* @__PURE__ */ jsx(ProcessRow, { node, selected: isSelected, series: series.get(node.row.key) }),
86
+ rowClass: (_node, isSelected) => isSelected ? "bg-primary/10 border-l-2 border-primary" : "hover:bg-accent"
87
+ }
88
+ );
89
+ }
90
+ export {
91
+ ProcessTraceTree
92
+ };
93
+ //# sourceMappingURL=ProcessTraceTree.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProcessTraceTree.js","sources":["../../../src/data/traces/ProcessTraceTree.tsx"],"sourcesContent":["import { useMemo } from \"react\";\nimport { UiDebug, UiServerProcess } from \"../../icons\";\nimport { formatBytes, formatDuration } from \"../../lib/format\";\nimport { cn } from \"../../lib/utils\";\nimport { processLabel, processStateColor, processStateIcon } from \"../diagnostics/utils\";\nimport { Icon } from \"../Icon\";\nimport { Tree } from \"../Tree\";\nimport { buildProcessForest, cpuSeriesByKey, flattenForest, type ProcessTreeNode } from \"./process-tree\";\nimport { Sparkline } from \"./Sparkline\";\nimport type { ProcessNodeRow, ProcessSampleRow } from \"./types\";\n\n/** Roots and their direct children start open; deeper levels open on demand or to reveal the selection. */\nconst OPEN_DEPTH = 2;\n\nexport type ProcessTraceTreeProps = {\n /** `process_tree` trace records, one per process. */\n nodes: ProcessNodeRow[];\n /** `process_samples` records; a process with at least two shows a CPU sparkline. */\n samples?: ProcessSampleRow[];\n /** Called with the `key` of the clicked process. */\n onSelect: (key: string) => void;\n /** `key` of the selected process. */\n selectedKey?: string | null;\n /** Shown when there are no processes. */\n emptyLabel?: string;\n /** Classes applied to the tree root. */\n className?: string;\n};\n\nfunction lifetimeMs(row: ProcessNodeRow): number {\n const end = row.exitedAt ?? row.lastSeenAt;\n return Date.parse(end) - Date.parse(row.startedAt);\n}\n\nfunction ProcessRow({ node, selected, series }: { node: ProcessTreeNode; selected: boolean; series: number[] | undefined }) {\n const { row, exited } = node;\n return (\n <div\n data-process-key={row.key}\n {...(exited ? { \"data-exited\": \"true\" } : {})}\n className={cn(\"flex min-w-0 flex-1 items-center gap-1.5\", exited && \"opacity-60\")}\n title={row.command}\n >\n <Icon\n icon={row.isRoot ? UiServerProcess : UiDebug}\n className={row.isRoot ? \"text-base text-blue-600\" : \"text-base text-muted-foreground\"}\n />\n <span className={cn(\"truncate\", selected ? \"font-semibold text-primary\" : \"font-medium text-foreground\")}>\n {processLabel({ pid: row.pid, name: row.name, command: row.command })}\n </span>\n <span className=\"shrink-0 text-xs text-muted-foreground\">pid {row.pid}</span>\n <span className=\"flex-1\" />\n {series ? <Sparkline values={series} label={`CPU % over ${series.length} samples`} /> : null}\n <span className={cn(\"inline-flex shrink-0 items-center gap-1 text-xs\", processStateColor(row.status))}>\n <Icon icon={processStateIcon(row.status)} />\n {row.status}\n </span>\n {exited ? (\n <>\n <span className=\"shrink-0 text-xs tabular-nums text-muted-foreground\" title=\"Lifetime\">\n {formatDuration(lifetimeMs(row))}\n </span>\n <span className=\"shrink-0 text-xs tabular-nums text-muted-foreground\" title=\"Peak CPU\">\n {`peak ${row.peakCpuPercent.toFixed(1)}%`}\n </span>\n <span className=\"shrink-0 text-xs tabular-nums text-muted-foreground\" title=\"Peak RSS\">\n {`peak ${formatBytes(row.peakRssBytes)}`}\n </span>\n </>\n ) : (\n <>\n <span className=\"shrink-0 text-xs tabular-nums text-muted-foreground\" title={`Peak ${row.peakCpuPercent.toFixed(1)}%`}>\n {`${row.cpuPercent.toFixed(1)}%`}\n </span>\n <span className=\"shrink-0 text-xs tabular-nums text-muted-foreground\" title={`Peak ${formatBytes(row.peakRssBytes)}`}>\n {formatBytes(row.rssBytes)}\n </span>\n </>\n )}\n </div>\n );\n}\n\n/**\n * The process tree of a `process_tree` trace: a forest keyed by `key` /\n * `parentKey`, where a process whose parent was never recorded is a root.\n * Exited processes are dimmed and show their lifetime and peak usage. It is\n * built on the same `Tree` primitive and process helpers as `DiagnosticsTree`,\n * which cannot take a forest, key selection or exit information.\n */\nexport function ProcessTraceTree({\n nodes,\n samples,\n onSelect,\n selectedKey,\n emptyLabel = \"No processes recorded\",\n className,\n}: ProcessTraceTreeProps) {\n const roots = useMemo(() => buildProcessForest(nodes), [nodes]);\n const series = useMemo(() => cpuSeriesByKey(samples ?? []), [samples]);\n const selected = useMemo(\n () => (selectedKey ? (flattenForest(roots).find((node) => node.row.key === selectedKey) ?? null) : null),\n [roots, selectedKey],\n );\n\n return (\n <Tree<ProcessTreeNode>\n roots={roots}\n {...(className ? { className } : {})}\n ariaLabel=\"Process tree\"\n empty={<div className=\"p-density-6 text-center text-muted-foreground text-sm\">{emptyLabel}</div>}\n getChildren={(node) => node.children}\n getKey={(node) => node.row.key}\n getSearchText={(node) => `${node.row.name} ${node.row.command} ${node.row.pid}`}\n selected={selected}\n revealSelected\n defaultOpen={(_node, depth) => depth < OPEN_DEPTH}\n onSelect={(node) => onSelect(node.row.key)}\n renderRow={({ node, selected: isSelected }) => (\n <ProcessRow node={node} selected={isSelected} series={series.get(node.row.key)} />\n )}\n rowClass={(_node, isSelected) =>\n isSelected ? \"bg-primary/10 border-l-2 border-primary\" : \"hover:bg-accent\"\n }\n />\n );\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const jsxRuntime = require("react/jsx-runtime");
4
+ const utils = require("../../lib/utils.cjs");
5
+ const processTree = require("./process-tree.cjs");
6
+ function Sparkline({ values, label, width = 64, height = 16, className }) {
7
+ const points = processTree.sparklinePoints(values, width, height);
8
+ if (!points) return null;
9
+ return /* @__PURE__ */ jsxRuntime.jsx(
10
+ "svg",
11
+ {
12
+ role: "img",
13
+ "aria-label": label,
14
+ viewBox: `0 0 ${width} ${height}`,
15
+ width,
16
+ height,
17
+ className: utils.cn("shrink-0 overflow-visible text-primary", className),
18
+ children: /* @__PURE__ */ jsxRuntime.jsx("polyline", { points, fill: "none", stroke: "currentColor", strokeWidth: 1.25, strokeLinejoin: "round" })
19
+ }
20
+ );
21
+ }
22
+ exports.Sparkline = Sparkline;
23
+ //# sourceMappingURL=Sparkline.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Sparkline.cjs","sources":["../../../src/data/traces/Sparkline.tsx"],"sourcesContent":["import { cn } from \"../../lib/utils\";\nimport { sparklinePoints } from \"./process-tree\";\n\nexport type SparklineProps = {\n /** Values in order; at least two are needed to draw a line. */\n values: number[];\n /** Accessible description of what the line plots. */\n label: string;\n width?: number;\n height?: number;\n /** Classes applied to the svg; the stroke follows the text colour. */\n className?: string;\n};\n\n/** A dependency-free inline SVG line, scaled so the largest value touches the top. */\nexport function Sparkline({ values, label, width = 64, height = 16, className }: SparklineProps) {\n const points = sparklinePoints(values, width, height);\n if (!points) return null;\n return (\n <svg\n role=\"img\"\n aria-label={label}\n viewBox={`0 0 ${width} ${height}`}\n width={width}\n height={height}\n className={cn(\"shrink-0 overflow-visible text-primary\", className)}\n >\n <polyline points={points} fill=\"none\" stroke=\"currentColor\" strokeWidth={1.25} strokeLinejoin=\"round\" />\n </svg>\n );\n}\n"],"names":["sparklinePoints","jsx","cn"],"mappings":";;;;;AAeO,SAAS,UAAU,EAAE,QAAQ,OAAO,QAAQ,IAAI,SAAS,IAAI,aAA6B;AAC/F,QAAM,SAASA,YAAAA,gBAAgB,QAAQ,OAAO,MAAM;AACpD,MAAI,CAAC,OAAQ,QAAO;AACpB,SACEC,2BAAAA;AAAAA,IAAC;AAAA,IAAA;AAAA,MACC,MAAK;AAAA,MACL,cAAY;AAAA,MACZ,SAAS,OAAO,KAAK,IAAI,MAAM;AAAA,MAC/B;AAAA,MACA;AAAA,MACA,WAAWC,MAAAA,GAAG,0CAA0C,SAAS;AAAA,MAEjE,UAAAD,2BAAAA,IAAC,YAAA,EAAS,QAAgB,MAAK,QAAO,QAAO,gBAAe,aAAa,MAAM,gBAAe,QAAA,CAAQ;AAAA,IAAA;AAAA,EAAA;AAG5G;;"}
@@ -0,0 +1,13 @@
1
+ export type SparklineProps = {
2
+ /** Values in order; at least two are needed to draw a line. */
3
+ values: number[];
4
+ /** Accessible description of what the line plots. */
5
+ label: string;
6
+ width?: number;
7
+ height?: number;
8
+ /** Classes applied to the svg; the stroke follows the text colour. */
9
+ className?: string;
10
+ };
11
+ /** A dependency-free inline SVG line, scaled so the largest value touches the top. */
12
+ export declare function Sparkline({ values, label, width, height, className }: SparklineProps): import("react").JSX.Element | null;
13
+ //# sourceMappingURL=Sparkline.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Sparkline.d.ts","sourceRoot":"","sources":["../../../src/data/traces/Sparkline.tsx"],"names":[],"mappings":"AAGA,MAAM,MAAM,cAAc,GAAG;IAC3B,+DAA+D;IAC/D,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,qDAAqD;IACrD,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sEAAsE;IACtE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,sFAAsF;AACtF,wBAAgB,SAAS,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,KAAU,EAAE,MAAW,EAAE,SAAS,EAAE,EAAE,cAAc,sCAe9F"}