@flanksource/clicky-ui 0.3.8 → 0.3.10

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 (78) hide show
  1. package/NOTICE.md +4 -0
  2. package/dist/data/ObjectGraph.cjs +39 -4
  3. package/dist/data/ObjectGraph.cjs.map +1 -1
  4. package/dist/data/ObjectGraph.d.ts +9 -1
  5. package/dist/data/ObjectGraph.d.ts.map +1 -1
  6. package/dist/data/ObjectGraph.js +39 -4
  7. package/dist/data/ObjectGraph.js.map +1 -1
  8. package/dist/data/test-runner/TestFilterBar.cjs +14 -9
  9. package/dist/data/test-runner/TestFilterBar.cjs.map +1 -1
  10. package/dist/data/test-runner/TestFilterBar.d.ts.map +1 -1
  11. package/dist/data/test-runner/TestFilterBar.js +15 -10
  12. package/dist/data/test-runner/TestFilterBar.js.map +1 -1
  13. package/dist/data/test-runner/TestRunSummary.cjs +1 -11
  14. package/dist/data/test-runner/TestRunSummary.cjs.map +1 -1
  15. package/dist/data/test-runner/TestRunSummary.d.ts.map +1 -1
  16. package/dist/data/test-runner/TestRunSummary.js +2 -12
  17. package/dist/data/test-runner/TestRunSummary.js.map +1 -1
  18. package/dist/data/test-runner/TestRunner.cjs +3 -12
  19. package/dist/data/test-runner/TestRunner.cjs.map +1 -1
  20. package/dist/data/test-runner/TestRunner.d.ts +8 -1
  21. package/dist/data/test-runner/TestRunner.d.ts.map +1 -1
  22. package/dist/data/test-runner/TestRunner.js +4 -13
  23. package/dist/data/test-runner/TestRunner.js.map +1 -1
  24. package/dist/data/test-runner/filterState.cjs +8 -0
  25. package/dist/data/test-runner/filterState.cjs.map +1 -1
  26. package/dist/data/test-runner/filterState.d.ts +2 -0
  27. package/dist/data/test-runner/filterState.d.ts.map +1 -1
  28. package/dist/data/test-runner/filterState.js +9 -1
  29. package/dist/data/test-runner/filterState.js.map +1 -1
  30. package/dist/data/test-runner/index.d.ts +2 -2
  31. package/dist/data/test-runner/index.d.ts.map +1 -1
  32. package/dist/data/test-runner/status.cjs +16 -0
  33. package/dist/data/test-runner/status.cjs.map +1 -1
  34. package/dist/data/test-runner/status.d.ts +9 -0
  35. package/dist/data/test-runner/status.d.ts.map +1 -1
  36. package/dist/data/test-runner/status.js +16 -0
  37. package/dist/data/test-runner/status.js.map +1 -1
  38. package/dist/data/version-info.cjs +3 -3
  39. package/dist/data/version-info.js +3 -3
  40. package/dist/data.cjs +27 -15
  41. package/dist/data.cjs.map +1 -1
  42. package/dist/data.js +14 -2
  43. package/dist/data.js.map +1 -1
  44. package/dist/icons/components/UiDebugContinue.cjs +10 -0
  45. package/dist/icons/components/UiDebugContinue.cjs.map +1 -0
  46. package/dist/icons/components/UiDebugContinue.d.ts +9 -0
  47. package/dist/icons/components/UiDebugContinue.d.ts.map +1 -0
  48. package/dist/icons/components/UiDebugContinue.js +10 -0
  49. package/dist/icons/components/UiDebugContinue.js.map +1 -0
  50. package/dist/icons/components/UiDebugStepInto.cjs +10 -0
  51. package/dist/icons/components/UiDebugStepInto.cjs.map +1 -0
  52. package/dist/icons/components/UiDebugStepInto.d.ts +9 -0
  53. package/dist/icons/components/UiDebugStepInto.d.ts.map +1 -0
  54. package/dist/icons/components/UiDebugStepInto.js +10 -0
  55. package/dist/icons/components/UiDebugStepInto.js.map +1 -0
  56. package/dist/icons/components/UiDebugStepOut.cjs +10 -0
  57. package/dist/icons/components/UiDebugStepOut.cjs.map +1 -0
  58. package/dist/icons/components/UiDebugStepOut.d.ts +9 -0
  59. package/dist/icons/components/UiDebugStepOut.d.ts.map +1 -0
  60. package/dist/icons/components/UiDebugStepOut.js +10 -0
  61. package/dist/icons/components/UiDebugStepOut.js.map +1 -0
  62. package/dist/icons/components/UiDebugStop.cjs +10 -0
  63. package/dist/icons/components/UiDebugStop.cjs.map +1 -0
  64. package/dist/icons/components/UiDebugStop.d.ts +9 -0
  65. package/dist/icons/components/UiDebugStop.d.ts.map +1 -0
  66. package/dist/icons/components/UiDebugStop.js +10 -0
  67. package/dist/icons/components/UiDebugStop.js.map +1 -0
  68. package/dist/icons/index.d.ts +4 -0
  69. package/dist/icons/index.d.ts.map +1 -1
  70. package/dist/icons.cjs +8 -0
  71. package/dist/icons.cjs.map +1 -1
  72. package/dist/icons.js +8 -0
  73. package/dist/icons.js.map +1 -1
  74. package/dist/index.cjs +27 -15
  75. package/dist/index.cjs.map +1 -1
  76. package/dist/index.js +14 -2
  77. package/dist/index.js.map +1 -1
  78. package/package.json +1 -1
package/NOTICE.md CHANGED
@@ -58,6 +58,10 @@ of its upstream source.
58
58
  | UiRestart | codicon:debug-restart |
59
59
  | UiStackFrameDot | codicon:debug-stackframe-dot |
60
60
  | UiDebugStepOver | codicon:debug-step-over |
61
+ | UiDebugContinue | codicon:debug-continue |
62
+ | UiDebugStepInto | codicon:debug-step-into |
63
+ | UiDebugStepOut | codicon:debug-step-out |
64
+ | UiDebugStop | codicon:debug-stop |
61
65
  | UiPlayCircle | ph:play-circle-light |
62
66
  | UiPlayCircleFilled | ph:play-circle-fill |
63
67
  | UiResizeVertical | ph:arrows-in-line-vertical-light |
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const jsxRuntime = require("react/jsx-runtime");
4
+ const utils = require("../lib/utils.cjs");
4
5
  const Tree = require("./Tree.cjs");
5
6
  function ObjectGraph({
6
7
  roots,
@@ -9,7 +10,9 @@ function ObjectGraph({
9
10
  showControls,
10
11
  defaultOpenDepth = 2,
11
12
  renderLabel,
12
- loadChildren
13
+ loadChildren,
14
+ onNodeSelect,
15
+ selectedId
13
16
  }) {
14
17
  return /* @__PURE__ */ jsxRuntime.jsx(
15
18
  Tree.Tree,
@@ -23,16 +26,48 @@ function ObjectGraph({
23
26
  ...showControls !== void 0 ? { showControls } : {},
24
27
  ...loadChildren ? { loadChildren, hasMoreChildren: (n) => n.expandable === true } : {},
25
28
  renderRow: ({ node, loading, error }) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 flex-1 flex-wrap items-center gap-2 font-mono text-xs", children: [
26
- renderLabel ? renderLabel(node) : /* @__PURE__ */ jsxRuntime.jsx(DefaultObjectLabel, { node }),
29
+ renderLabel ? renderLabel(node) : /* @__PURE__ */ jsxRuntime.jsx(
30
+ DefaultObjectLabel,
31
+ {
32
+ node,
33
+ selected: selectedId != null && node.id === selectedId,
34
+ ...onNodeSelect ? { onSelect: onNodeSelect } : {}
35
+ }
36
+ ),
27
37
  loading && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 text-muted-foreground", children: "…" }),
28
38
  error != null && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "break-all text-red-600", children: error instanceof Error ? error.message : String(error) })
29
39
  ] })
30
40
  }
31
41
  );
32
42
  }
33
- function DefaultObjectLabel({ node }) {
43
+ function DefaultObjectLabel({
44
+ node,
45
+ onSelect,
46
+ selected = false
47
+ }) {
48
+ const label = onSelect ? (
49
+ // A button so the label is a first-class click target; stopPropagation keeps
50
+ // the row's onClick (which toggles the node) from firing, so selecting a node
51
+ // never expands or collapses it.
52
+ /* @__PURE__ */ jsxRuntime.jsx(
53
+ "button",
54
+ {
55
+ type: "button",
56
+ onClick: (e) => {
57
+ e.stopPropagation();
58
+ onSelect(node);
59
+ },
60
+ title: "Select this path",
61
+ className: utils.cn(
62
+ "rounded px-0.5 text-left text-muted-foreground hover:text-foreground hover:underline",
63
+ selected && "bg-primary/15 text-foreground"
64
+ ),
65
+ children: node.label
66
+ }
67
+ )
68
+ ) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: node.label });
34
69
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
35
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: node.label }),
70
+ label,
36
71
  node.type && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-muted-foreground/60", children: [
37
72
  "@",
38
73
  node.type
@@ -1 +1 @@
1
- {"version":3,"file":"ObjectGraph.cjs","sources":["../../src/data/ObjectGraph.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { Tree } from \"./Tree\";\n\n// ObjectGraph renders a generic, type-agnostic expandable object/value\n// inspector — a bean's fields, a map's entries, a list's elements, a scalar\n// leaf. It is the shared home for what used to be per-feature object viewers\n// (e.g. an OGNL inspector): any producer maps its data into ObjectGraphNode and\n// gets the same tree, search, and lazy-expansion behaviour.\n\nexport type ObjectGraphNode = {\n /** Stable unique key within the tree. */\n id: string;\n /** Display label (field name, map key, index, …). */\n label: string;\n /** Optional type annotation rendered as `@type`. */\n type?: string;\n /** Scalar value for a leaf; absent for containers. */\n value?: string | number | boolean | null;\n /** Free-form node kind hint (object|map|list|scalar|…). */\n kind?: string;\n /** Accessor path from the root (used by lazy expansion). */\n path?: string;\n /** Verbatim preview for an opaque node with no structured children. */\n raw?: string;\n /** Node can be expanded via loadChildren even with no inline children. */\n expandable?: boolean;\n /** Caller-opaque context (carried through, not rendered by default). */\n metadata?: Record<string, unknown>;\n children?: ObjectGraphNode[];\n};\n\nexport type ObjectGraphProps<T extends ObjectGraphNode = ObjectGraphNode> = {\n roots: T[];\n className?: string;\n empty?: ReactNode;\n showControls?: boolean;\n /** Open nodes shallower than this depth on first render (default 2). */\n defaultOpenDepth?: number;\n /** Override the row label rendering. */\n renderLabel?: (node: T) => ReactNode;\n /**\n * Lazily fetch a node's children the first time an `expandable` node opens.\n * When omitted, only inline `children` are shown.\n */\n loadChildren?: (node: T) => Promise<T[]>;\n};\n\nexport function ObjectGraph<T extends ObjectGraphNode = ObjectGraphNode>({\n roots,\n className,\n empty,\n showControls,\n defaultOpenDepth = 2,\n renderLabel,\n loadChildren,\n}: ObjectGraphProps<T>) {\n return (\n <Tree<T>\n roots={roots}\n getKey={(n) => n.id}\n getChildren={(n) => n.children as T[] | undefined}\n defaultOpen={(_n, depth) => depth < defaultOpenDepth}\n {...(className !== undefined ? { className } : {})}\n {...(empty !== undefined ? { empty } : {})}\n {...(showControls !== undefined ? { showControls } : {})}\n {...(loadChildren ? { loadChildren, hasMoreChildren: (n: T) => n.expandable === true } : {})}\n renderRow={({ node, loading, error }) => (\n <div className=\"flex min-w-0 flex-1 flex-wrap items-center gap-2 font-mono text-xs\">\n {renderLabel ? renderLabel(node) : <DefaultObjectLabel node={node} />}\n {loading && <span className=\"shrink-0 text-muted-foreground\">…</span>}\n {error != null && (\n <span className=\"break-all text-red-600\">\n {error instanceof Error ? error.message : String(error)}\n </span>\n )}\n </div>\n )}\n />\n );\n}\n\nfunction DefaultObjectLabel({ node }: { node: ObjectGraphNode }) {\n return (\n <>\n <span className=\"text-muted-foreground\">{node.label}</span>\n {node.type && <span className=\"text-muted-foreground/60\">@{node.type}</span>}\n {node.value != null ? (\n <span className=\"break-all text-foreground\">{String(node.value)}</span>\n ) : node.raw && !node.children ? (\n <span className=\"break-all italic text-muted-foreground/70\">{node.raw}</span>\n ) : null}\n </>\n );\n}\n"],"names":["jsx","Tree","jsxs","Fragment"],"mappings":";;;;AA+CO,SAAS,YAAyD;AAAA,EACvE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,mBAAmB;AAAA,EACnB;AAAA,EACA;AACF,GAAwB;AACtB,SACEA,2BAAAA;AAAAA,IAACC,KAAAA;AAAAA,IAAA;AAAA,MACC;AAAA,MACA,QAAQ,CAAC,MAAM,EAAE;AAAA,MACjB,aAAa,CAAC,MAAM,EAAE;AAAA,MACtB,aAAa,CAAC,IAAI,UAAU,QAAQ;AAAA,MACnC,GAAI,cAAc,SAAY,EAAE,UAAA,IAAc,CAAA;AAAA,MAC9C,GAAI,UAAU,SAAY,EAAE,MAAA,IAAU,CAAA;AAAA,MACtC,GAAI,iBAAiB,SAAY,EAAE,aAAA,IAAiB,CAAA;AAAA,MACpD,GAAI,eAAe,EAAE,cAAc,iBAAiB,CAAC,MAAS,EAAE,eAAe,KAAA,IAAS,CAAA;AAAA,MACzF,WAAW,CAAC,EAAE,MAAM,SAAS,YAC3BC,2BAAAA,KAAC,OAAA,EAAI,WAAU,sEACZ,UAAA;AAAA,QAAA,cAAc,YAAY,IAAI,IAAIF,2BAAAA,IAAC,sBAAmB,MAAY;AAAA,QAClE,WAAWA,2BAAAA,IAAC,QAAA,EAAK,WAAU,kCAAiC,UAAA,KAAC;AAAA,QAC7D,SAAS,QACRA,2BAAAA,IAAC,QAAA,EAAK,WAAU,0BACb,UAAA,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,EAAA,CACxD;AAAA,MAAA,EAAA,CAEJ;AAAA,IAAA;AAAA,EAAA;AAIR;AAEA,SAAS,mBAAmB,EAAE,QAAmC;AAC/D,SACEE,2BAAAA,KAAAC,qBAAA,EACE,UAAA;AAAA,IAAAH,2BAAAA,IAAC,QAAA,EAAK,WAAU,yBAAyB,UAAA,KAAK,OAAM;AAAA,IACnD,KAAK,QAAQE,gCAAC,QAAA,EAAK,WAAU,4BAA2B,UAAA;AAAA,MAAA;AAAA,MAAE,KAAK;AAAA,IAAA,GAAK;AAAA,IACpE,KAAK,SAAS,OACbF,2BAAAA,IAAC,UAAK,WAAU,6BAA6B,UAAA,OAAO,KAAK,KAAK,EAAA,CAAE,IAC9D,KAAK,OAAO,CAAC,KAAK,WACpBA,2BAAAA,IAAC,UAAK,WAAU,6CAA6C,UAAA,KAAK,IAAA,CAAI,IACpE;AAAA,EAAA,GACN;AAEJ;;"}
1
+ {"version":3,"file":"ObjectGraph.cjs","sources":["../../src/data/ObjectGraph.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Tree } from \"./Tree\";\n\n// ObjectGraph renders a generic, type-agnostic expandable object/value\n// inspector — a bean's fields, a map's entries, a list's elements, a scalar\n// leaf. It is the shared home for what used to be per-feature object viewers\n// (e.g. an OGNL inspector): any producer maps its data into ObjectGraphNode and\n// gets the same tree, search, and lazy-expansion behaviour.\n\nexport type ObjectGraphNode = {\n /** Stable unique key within the tree. */\n id: string;\n /** Display label (field name, map key, index, …). */\n label: string;\n /** Optional type annotation rendered as `@type`. */\n type?: string;\n /** Scalar value for a leaf; absent for containers. */\n value?: string | number | boolean | null;\n /** Free-form node kind hint (object|map|list|scalar|…). */\n kind?: string;\n /** Accessor path from the root (used by lazy expansion). */\n path?: string;\n /** Verbatim preview for an opaque node with no structured children. */\n raw?: string;\n /** Node can be expanded via loadChildren even with no inline children. */\n expandable?: boolean;\n /** Caller-opaque context (carried through, not rendered by default). */\n metadata?: Record<string, unknown>;\n children?: ObjectGraphNode[];\n};\n\nexport type ObjectGraphProps<T extends ObjectGraphNode = ObjectGraphNode> = {\n roots: T[];\n className?: string;\n empty?: ReactNode;\n showControls?: boolean;\n /** Open nodes shallower than this depth on first render (default 2). */\n defaultOpenDepth?: number;\n /** Override the row label rendering. */\n renderLabel?: (node: T) => ReactNode;\n /**\n * Lazily fetch a node's children the first time an `expandable` node opens.\n * When omitted, only inline `children` are shown.\n */\n loadChildren?: (node: T) => Promise<T[]>;\n /**\n * Called when a node's label is clicked. This makes the label an actionable\n * target (e.g. copy/drill its `path`) **without toggling expansion** — the\n * chevron stays the only expand/collapse control. Omit for a static tree.\n */\n onNodeSelect?: (node: T) => void;\n /** `id` of the currently-selected node; its label is highlighted. */\n selectedId?: string;\n};\n\nexport function ObjectGraph<T extends ObjectGraphNode = ObjectGraphNode>({\n roots,\n className,\n empty,\n showControls,\n defaultOpenDepth = 2,\n renderLabel,\n loadChildren,\n onNodeSelect,\n selectedId,\n}: ObjectGraphProps<T>) {\n return (\n <Tree<T>\n roots={roots}\n getKey={(n) => n.id}\n getChildren={(n) => n.children as T[] | undefined}\n defaultOpen={(_n, depth) => depth < defaultOpenDepth}\n {...(className !== undefined ? { className } : {})}\n {...(empty !== undefined ? { empty } : {})}\n {...(showControls !== undefined ? { showControls } : {})}\n {...(loadChildren ? { loadChildren, hasMoreChildren: (n: T) => n.expandable === true } : {})}\n renderRow={({ node, loading, error }) => (\n <div className=\"flex min-w-0 flex-1 flex-wrap items-center gap-2 font-mono text-xs\">\n {renderLabel ? (\n renderLabel(node)\n ) : (\n <DefaultObjectLabel\n node={node}\n selected={selectedId != null && node.id === selectedId}\n {...(onNodeSelect ? { onSelect: onNodeSelect } : {})}\n />\n )}\n {loading && <span className=\"shrink-0 text-muted-foreground\">…</span>}\n {error != null && (\n <span className=\"break-all text-red-600\">\n {error instanceof Error ? error.message : String(error)}\n </span>\n )}\n </div>\n )}\n />\n );\n}\n\nfunction DefaultObjectLabel<T extends ObjectGraphNode>({\n node,\n onSelect,\n selected = false,\n}: {\n node: T;\n onSelect?: (node: T) => void;\n selected?: boolean;\n}) {\n const label = onSelect ? (\n // A button so the label is a first-class click target; stopPropagation keeps\n // the row's onClick (which toggles the node) from firing, so selecting a node\n // never expands or collapses it.\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onSelect(node);\n }}\n title=\"Select this path\"\n className={cn(\n \"rounded px-0.5 text-left text-muted-foreground hover:text-foreground hover:underline\",\n selected && \"bg-primary/15 text-foreground\",\n )}\n >\n {node.label}\n </button>\n ) : (\n <span className=\"text-muted-foreground\">{node.label}</span>\n );\n return (\n <>\n {label}\n {node.type && <span className=\"text-muted-foreground/60\">@{node.type}</span>}\n {node.value != null ? (\n <span className=\"break-all text-foreground\">{String(node.value)}</span>\n ) : node.raw && !node.children ? (\n <span className=\"break-all italic text-muted-foreground/70\">{node.raw}</span>\n ) : null}\n </>\n );\n}\n"],"names":["jsx","Tree","jsxs","cn","Fragment"],"mappings":";;;;;AAwDO,SAAS,YAAyD;AAAA,EACvE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,mBAAmB;AAAA,EACnB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAwB;AACtB,SACEA,2BAAAA;AAAAA,IAACC,KAAAA;AAAAA,IAAA;AAAA,MACC;AAAA,MACA,QAAQ,CAAC,MAAM,EAAE;AAAA,MACjB,aAAa,CAAC,MAAM,EAAE;AAAA,MACtB,aAAa,CAAC,IAAI,UAAU,QAAQ;AAAA,MACnC,GAAI,cAAc,SAAY,EAAE,UAAA,IAAc,CAAA;AAAA,MAC9C,GAAI,UAAU,SAAY,EAAE,MAAA,IAAU,CAAA;AAAA,MACtC,GAAI,iBAAiB,SAAY,EAAE,aAAA,IAAiB,CAAA;AAAA,MACpD,GAAI,eAAe,EAAE,cAAc,iBAAiB,CAAC,MAAS,EAAE,eAAe,KAAA,IAAS,CAAA;AAAA,MACzF,WAAW,CAAC,EAAE,MAAM,SAAS,YAC3BC,2BAAAA,KAAC,OAAA,EAAI,WAAU,sEACZ,UAAA;AAAA,QAAA,cACC,YAAY,IAAI,IAEhBF,2BAAAA;AAAAA,UAAC;AAAA,UAAA;AAAA,YACC;AAAA,YACA,UAAU,cAAc,QAAQ,KAAK,OAAO;AAAA,YAC3C,GAAI,eAAe,EAAE,UAAU,iBAAiB,CAAA;AAAA,UAAC;AAAA,QAAA;AAAA,QAGrD,WAAWA,2BAAAA,IAAC,QAAA,EAAK,WAAU,kCAAiC,UAAA,KAAC;AAAA,QAC7D,SAAS,QACRA,2BAAAA,IAAC,QAAA,EAAK,WAAU,0BACb,UAAA,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,EAAA,CACxD;AAAA,MAAA,EAAA,CAEJ;AAAA,IAAA;AAAA,EAAA;AAIR;AAEA,SAAS,mBAA8C;AAAA,EACrD;AAAA,EACA;AAAA,EACA,WAAW;AACb,GAIG;AACD,QAAM,QAAQ;AAAA;AAAA;AAAA;AAAA,IAIZA,2BAAAA;AAAAA,MAAC;AAAA,MAAA;AAAA,QACC,MAAK;AAAA,QACL,SAAS,CAAC,MAAM;AACd,YAAE,gBAAA;AACF,mBAAS,IAAI;AAAA,QACf;AAAA,QACA,OAAM;AAAA,QACN,WAAWG,MAAAA;AAAAA,UACT;AAAA,UACA,YAAY;AAAA,QAAA;AAAA,QAGb,UAAA,KAAK;AAAA,MAAA;AAAA,IAAA;AAAA,MAGRH,2BAAAA,IAAC,QAAA,EAAK,WAAU,yBAAyB,eAAK,OAAM;AAEtD,SACEE,2BAAAA,KAAAE,qBAAA,EACG,UAAA;AAAA,IAAA;AAAA,IACA,KAAK,QAAQF,gCAAC,QAAA,EAAK,WAAU,4BAA2B,UAAA;AAAA,MAAA;AAAA,MAAE,KAAK;AAAA,IAAA,GAAK;AAAA,IACpE,KAAK,SAAS,OACbF,2BAAAA,IAAC,UAAK,WAAU,6BAA6B,UAAA,OAAO,KAAK,KAAK,EAAA,CAAE,IAC9D,KAAK,OAAO,CAAC,KAAK,WACpBA,2BAAAA,IAAC,UAAK,WAAU,6CAA6C,UAAA,KAAK,IAAA,CAAI,IACpE;AAAA,EAAA,GACN;AAEJ;;"}
@@ -34,6 +34,14 @@ export type ObjectGraphProps<T extends ObjectGraphNode = ObjectGraphNode> = {
34
34
  * When omitted, only inline `children` are shown.
35
35
  */
36
36
  loadChildren?: (node: T) => Promise<T[]>;
37
+ /**
38
+ * Called when a node's label is clicked. This makes the label an actionable
39
+ * target (e.g. copy/drill its `path`) **without toggling expansion** — the
40
+ * chevron stays the only expand/collapse control. Omit for a static tree.
41
+ */
42
+ onNodeSelect?: (node: T) => void;
43
+ /** `id` of the currently-selected node; its label is highlighted. */
44
+ selectedId?: string;
37
45
  };
38
- export declare function ObjectGraph<T extends ObjectGraphNode = ObjectGraphNode>({ roots, className, empty, showControls, defaultOpenDepth, renderLabel, loadChildren, }: ObjectGraphProps<T>): import("react/jsx-runtime").JSX.Element;
46
+ export declare function ObjectGraph<T extends ObjectGraphNode = ObjectGraphNode>({ roots, className, empty, showControls, defaultOpenDepth, renderLabel, loadChildren, onNodeSelect, selectedId, }: ObjectGraphProps<T>): import("react/jsx-runtime").JSX.Element;
39
47
  //# sourceMappingURL=ObjectGraph.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ObjectGraph.d.ts","sourceRoot":"","sources":["../../src/data/ObjectGraph.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AASvC,MAAM,MAAM,eAAe,GAAG;IAC5B,yCAAyC;IACzC,EAAE,EAAE,MAAM,CAAC;IACX,qDAAqD;IACrD,KAAK,EAAE,MAAM,CAAC;IACd,oDAAoD;IACpD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,sDAAsD;IACtD,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC;IACzC,2DAA2D;IAC3D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,4DAA4D;IAC5D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,uEAAuE;IACvE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,0EAA0E;IAC1E,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,wEAAwE;IACxE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,QAAQ,CAAC,EAAE,eAAe,EAAE,CAAC;CAC9B,CAAC;AAEF,MAAM,MAAM,gBAAgB,CAAC,CAAC,SAAS,eAAe,GAAG,eAAe,IAAI;IAC1E,KAAK,EAAE,CAAC,EAAE,CAAC;IACX,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,wEAAwE;IACxE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,wCAAwC;IACxC,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,SAAS,CAAC;IACrC;;;OAGG;IACH,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC;CAC1C,CAAC;AAEF,wBAAgB,WAAW,CAAC,CAAC,SAAS,eAAe,GAAG,eAAe,EAAE,EACvE,KAAK,EACL,SAAS,EACT,KAAK,EACL,YAAY,EACZ,gBAAoB,EACpB,WAAW,EACX,YAAY,GACb,EAAE,gBAAgB,CAAC,CAAC,CAAC,2CAwBrB"}
1
+ {"version":3,"file":"ObjectGraph.d.ts","sourceRoot":"","sources":["../../src/data/ObjectGraph.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAUvC,MAAM,MAAM,eAAe,GAAG;IAC5B,yCAAyC;IACzC,EAAE,EAAE,MAAM,CAAC;IACX,qDAAqD;IACrD,KAAK,EAAE,MAAM,CAAC;IACd,oDAAoD;IACpD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,sDAAsD;IACtD,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC;IACzC,2DAA2D;IAC3D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,4DAA4D;IAC5D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,uEAAuE;IACvE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,0EAA0E;IAC1E,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,wEAAwE;IACxE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,QAAQ,CAAC,EAAE,eAAe,EAAE,CAAC;CAC9B,CAAC;AAEF,MAAM,MAAM,gBAAgB,CAAC,CAAC,SAAS,eAAe,GAAG,eAAe,IAAI;IAC1E,KAAK,EAAE,CAAC,EAAE,CAAC;IACX,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,wEAAwE;IACxE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,wCAAwC;IACxC,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,SAAS,CAAC;IACrC;;;OAGG;IACH,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC;IACzC;;;;OAIG;IACH,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,CAAC;IACjC,qEAAqE;IACrE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,wBAAgB,WAAW,CAAC,CAAC,SAAS,eAAe,GAAG,eAAe,EAAE,EACvE,KAAK,EACL,SAAS,EACT,KAAK,EACL,YAAY,EACZ,gBAAoB,EACpB,WAAW,EACX,YAAY,EACZ,YAAY,EACZ,UAAU,GACX,EAAE,gBAAgB,CAAC,CAAC,CAAC,2CAgCrB"}
@@ -1,4 +1,5 @@
1
1
  import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
+ import { cn } from "../lib/utils.js";
2
3
  import { Tree } from "./Tree.js";
3
4
  function ObjectGraph({
4
5
  roots,
@@ -7,7 +8,9 @@ function ObjectGraph({
7
8
  showControls,
8
9
  defaultOpenDepth = 2,
9
10
  renderLabel,
10
- loadChildren
11
+ loadChildren,
12
+ onNodeSelect,
13
+ selectedId
11
14
  }) {
12
15
  return /* @__PURE__ */ jsx(
13
16
  Tree,
@@ -21,16 +24,48 @@ function ObjectGraph({
21
24
  ...showControls !== void 0 ? { showControls } : {},
22
25
  ...loadChildren ? { loadChildren, hasMoreChildren: (n) => n.expandable === true } : {},
23
26
  renderRow: ({ node, loading, error }) => /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 flex-wrap items-center gap-2 font-mono text-xs", children: [
24
- renderLabel ? renderLabel(node) : /* @__PURE__ */ jsx(DefaultObjectLabel, { node }),
27
+ renderLabel ? renderLabel(node) : /* @__PURE__ */ jsx(
28
+ DefaultObjectLabel,
29
+ {
30
+ node,
31
+ selected: selectedId != null && node.id === selectedId,
32
+ ...onNodeSelect ? { onSelect: onNodeSelect } : {}
33
+ }
34
+ ),
25
35
  loading && /* @__PURE__ */ jsx("span", { className: "shrink-0 text-muted-foreground", children: "…" }),
26
36
  error != null && /* @__PURE__ */ jsx("span", { className: "break-all text-red-600", children: error instanceof Error ? error.message : String(error) })
27
37
  ] })
28
38
  }
29
39
  );
30
40
  }
31
- function DefaultObjectLabel({ node }) {
41
+ function DefaultObjectLabel({
42
+ node,
43
+ onSelect,
44
+ selected = false
45
+ }) {
46
+ const label = onSelect ? (
47
+ // A button so the label is a first-class click target; stopPropagation keeps
48
+ // the row's onClick (which toggles the node) from firing, so selecting a node
49
+ // never expands or collapses it.
50
+ /* @__PURE__ */ jsx(
51
+ "button",
52
+ {
53
+ type: "button",
54
+ onClick: (e) => {
55
+ e.stopPropagation();
56
+ onSelect(node);
57
+ },
58
+ title: "Select this path",
59
+ className: cn(
60
+ "rounded px-0.5 text-left text-muted-foreground hover:text-foreground hover:underline",
61
+ selected && "bg-primary/15 text-foreground"
62
+ ),
63
+ children: node.label
64
+ }
65
+ )
66
+ ) : /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: node.label });
32
67
  return /* @__PURE__ */ jsxs(Fragment, { children: [
33
- /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: node.label }),
68
+ label,
34
69
  node.type && /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground/60", children: [
35
70
  "@",
36
71
  node.type
@@ -1 +1 @@
1
- {"version":3,"file":"ObjectGraph.js","sources":["../../src/data/ObjectGraph.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { Tree } from \"./Tree\";\n\n// ObjectGraph renders a generic, type-agnostic expandable object/value\n// inspector — a bean's fields, a map's entries, a list's elements, a scalar\n// leaf. It is the shared home for what used to be per-feature object viewers\n// (e.g. an OGNL inspector): any producer maps its data into ObjectGraphNode and\n// gets the same tree, search, and lazy-expansion behaviour.\n\nexport type ObjectGraphNode = {\n /** Stable unique key within the tree. */\n id: string;\n /** Display label (field name, map key, index, …). */\n label: string;\n /** Optional type annotation rendered as `@type`. */\n type?: string;\n /** Scalar value for a leaf; absent for containers. */\n value?: string | number | boolean | null;\n /** Free-form node kind hint (object|map|list|scalar|…). */\n kind?: string;\n /** Accessor path from the root (used by lazy expansion). */\n path?: string;\n /** Verbatim preview for an opaque node with no structured children. */\n raw?: string;\n /** Node can be expanded via loadChildren even with no inline children. */\n expandable?: boolean;\n /** Caller-opaque context (carried through, not rendered by default). */\n metadata?: Record<string, unknown>;\n children?: ObjectGraphNode[];\n};\n\nexport type ObjectGraphProps<T extends ObjectGraphNode = ObjectGraphNode> = {\n roots: T[];\n className?: string;\n empty?: ReactNode;\n showControls?: boolean;\n /** Open nodes shallower than this depth on first render (default 2). */\n defaultOpenDepth?: number;\n /** Override the row label rendering. */\n renderLabel?: (node: T) => ReactNode;\n /**\n * Lazily fetch a node's children the first time an `expandable` node opens.\n * When omitted, only inline `children` are shown.\n */\n loadChildren?: (node: T) => Promise<T[]>;\n};\n\nexport function ObjectGraph<T extends ObjectGraphNode = ObjectGraphNode>({\n roots,\n className,\n empty,\n showControls,\n defaultOpenDepth = 2,\n renderLabel,\n loadChildren,\n}: ObjectGraphProps<T>) {\n return (\n <Tree<T>\n roots={roots}\n getKey={(n) => n.id}\n getChildren={(n) => n.children as T[] | undefined}\n defaultOpen={(_n, depth) => depth < defaultOpenDepth}\n {...(className !== undefined ? { className } : {})}\n {...(empty !== undefined ? { empty } : {})}\n {...(showControls !== undefined ? { showControls } : {})}\n {...(loadChildren ? { loadChildren, hasMoreChildren: (n: T) => n.expandable === true } : {})}\n renderRow={({ node, loading, error }) => (\n <div className=\"flex min-w-0 flex-1 flex-wrap items-center gap-2 font-mono text-xs\">\n {renderLabel ? renderLabel(node) : <DefaultObjectLabel node={node} />}\n {loading && <span className=\"shrink-0 text-muted-foreground\">…</span>}\n {error != null && (\n <span className=\"break-all text-red-600\">\n {error instanceof Error ? error.message : String(error)}\n </span>\n )}\n </div>\n )}\n />\n );\n}\n\nfunction DefaultObjectLabel({ node }: { node: ObjectGraphNode }) {\n return (\n <>\n <span className=\"text-muted-foreground\">{node.label}</span>\n {node.type && <span className=\"text-muted-foreground/60\">@{node.type}</span>}\n {node.value != null ? (\n <span className=\"break-all text-foreground\">{String(node.value)}</span>\n ) : node.raw && !node.children ? (\n <span className=\"break-all italic text-muted-foreground/70\">{node.raw}</span>\n ) : null}\n </>\n );\n}\n"],"names":[],"mappings":";;AA+CO,SAAS,YAAyD;AAAA,EACvE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,mBAAmB;AAAA,EACnB;AAAA,EACA;AACF,GAAwB;AACtB,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC;AAAA,MACA,QAAQ,CAAC,MAAM,EAAE;AAAA,MACjB,aAAa,CAAC,MAAM,EAAE;AAAA,MACtB,aAAa,CAAC,IAAI,UAAU,QAAQ;AAAA,MACnC,GAAI,cAAc,SAAY,EAAE,UAAA,IAAc,CAAA;AAAA,MAC9C,GAAI,UAAU,SAAY,EAAE,MAAA,IAAU,CAAA;AAAA,MACtC,GAAI,iBAAiB,SAAY,EAAE,aAAA,IAAiB,CAAA;AAAA,MACpD,GAAI,eAAe,EAAE,cAAc,iBAAiB,CAAC,MAAS,EAAE,eAAe,KAAA,IAAS,CAAA;AAAA,MACzF,WAAW,CAAC,EAAE,MAAM,SAAS,YAC3B,qBAAC,OAAA,EAAI,WAAU,sEACZ,UAAA;AAAA,QAAA,cAAc,YAAY,IAAI,IAAI,oBAAC,sBAAmB,MAAY;AAAA,QAClE,WAAW,oBAAC,QAAA,EAAK,WAAU,kCAAiC,UAAA,KAAC;AAAA,QAC7D,SAAS,QACR,oBAAC,QAAA,EAAK,WAAU,0BACb,UAAA,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,EAAA,CACxD;AAAA,MAAA,EAAA,CAEJ;AAAA,IAAA;AAAA,EAAA;AAIR;AAEA,SAAS,mBAAmB,EAAE,QAAmC;AAC/D,SACE,qBAAA,UAAA,EACE,UAAA;AAAA,IAAA,oBAAC,QAAA,EAAK,WAAU,yBAAyB,UAAA,KAAK,OAAM;AAAA,IACnD,KAAK,QAAQ,qBAAC,QAAA,EAAK,WAAU,4BAA2B,UAAA;AAAA,MAAA;AAAA,MAAE,KAAK;AAAA,IAAA,GAAK;AAAA,IACpE,KAAK,SAAS,OACb,oBAAC,UAAK,WAAU,6BAA6B,UAAA,OAAO,KAAK,KAAK,EAAA,CAAE,IAC9D,KAAK,OAAO,CAAC,KAAK,WACpB,oBAAC,UAAK,WAAU,6CAA6C,UAAA,KAAK,IAAA,CAAI,IACpE;AAAA,EAAA,GACN;AAEJ;"}
1
+ {"version":3,"file":"ObjectGraph.js","sources":["../../src/data/ObjectGraph.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Tree } from \"./Tree\";\n\n// ObjectGraph renders a generic, type-agnostic expandable object/value\n// inspector — a bean's fields, a map's entries, a list's elements, a scalar\n// leaf. It is the shared home for what used to be per-feature object viewers\n// (e.g. an OGNL inspector): any producer maps its data into ObjectGraphNode and\n// gets the same tree, search, and lazy-expansion behaviour.\n\nexport type ObjectGraphNode = {\n /** Stable unique key within the tree. */\n id: string;\n /** Display label (field name, map key, index, …). */\n label: string;\n /** Optional type annotation rendered as `@type`. */\n type?: string;\n /** Scalar value for a leaf; absent for containers. */\n value?: string | number | boolean | null;\n /** Free-form node kind hint (object|map|list|scalar|…). */\n kind?: string;\n /** Accessor path from the root (used by lazy expansion). */\n path?: string;\n /** Verbatim preview for an opaque node with no structured children. */\n raw?: string;\n /** Node can be expanded via loadChildren even with no inline children. */\n expandable?: boolean;\n /** Caller-opaque context (carried through, not rendered by default). */\n metadata?: Record<string, unknown>;\n children?: ObjectGraphNode[];\n};\n\nexport type ObjectGraphProps<T extends ObjectGraphNode = ObjectGraphNode> = {\n roots: T[];\n className?: string;\n empty?: ReactNode;\n showControls?: boolean;\n /** Open nodes shallower than this depth on first render (default 2). */\n defaultOpenDepth?: number;\n /** Override the row label rendering. */\n renderLabel?: (node: T) => ReactNode;\n /**\n * Lazily fetch a node's children the first time an `expandable` node opens.\n * When omitted, only inline `children` are shown.\n */\n loadChildren?: (node: T) => Promise<T[]>;\n /**\n * Called when a node's label is clicked. This makes the label an actionable\n * target (e.g. copy/drill its `path`) **without toggling expansion** — the\n * chevron stays the only expand/collapse control. Omit for a static tree.\n */\n onNodeSelect?: (node: T) => void;\n /** `id` of the currently-selected node; its label is highlighted. */\n selectedId?: string;\n};\n\nexport function ObjectGraph<T extends ObjectGraphNode = ObjectGraphNode>({\n roots,\n className,\n empty,\n showControls,\n defaultOpenDepth = 2,\n renderLabel,\n loadChildren,\n onNodeSelect,\n selectedId,\n}: ObjectGraphProps<T>) {\n return (\n <Tree<T>\n roots={roots}\n getKey={(n) => n.id}\n getChildren={(n) => n.children as T[] | undefined}\n defaultOpen={(_n, depth) => depth < defaultOpenDepth}\n {...(className !== undefined ? { className } : {})}\n {...(empty !== undefined ? { empty } : {})}\n {...(showControls !== undefined ? { showControls } : {})}\n {...(loadChildren ? { loadChildren, hasMoreChildren: (n: T) => n.expandable === true } : {})}\n renderRow={({ node, loading, error }) => (\n <div className=\"flex min-w-0 flex-1 flex-wrap items-center gap-2 font-mono text-xs\">\n {renderLabel ? (\n renderLabel(node)\n ) : (\n <DefaultObjectLabel\n node={node}\n selected={selectedId != null && node.id === selectedId}\n {...(onNodeSelect ? { onSelect: onNodeSelect } : {})}\n />\n )}\n {loading && <span className=\"shrink-0 text-muted-foreground\">…</span>}\n {error != null && (\n <span className=\"break-all text-red-600\">\n {error instanceof Error ? error.message : String(error)}\n </span>\n )}\n </div>\n )}\n />\n );\n}\n\nfunction DefaultObjectLabel<T extends ObjectGraphNode>({\n node,\n onSelect,\n selected = false,\n}: {\n node: T;\n onSelect?: (node: T) => void;\n selected?: boolean;\n}) {\n const label = onSelect ? (\n // A button so the label is a first-class click target; stopPropagation keeps\n // the row's onClick (which toggles the node) from firing, so selecting a node\n // never expands or collapses it.\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onSelect(node);\n }}\n title=\"Select this path\"\n className={cn(\n \"rounded px-0.5 text-left text-muted-foreground hover:text-foreground hover:underline\",\n selected && \"bg-primary/15 text-foreground\",\n )}\n >\n {node.label}\n </button>\n ) : (\n <span className=\"text-muted-foreground\">{node.label}</span>\n );\n return (\n <>\n {label}\n {node.type && <span className=\"text-muted-foreground/60\">@{node.type}</span>}\n {node.value != null ? (\n <span className=\"break-all text-foreground\">{String(node.value)}</span>\n ) : node.raw && !node.children ? (\n <span className=\"break-all italic text-muted-foreground/70\">{node.raw}</span>\n ) : null}\n </>\n );\n}\n"],"names":[],"mappings":";;;AAwDO,SAAS,YAAyD;AAAA,EACvE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,mBAAmB;AAAA,EACnB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAwB;AACtB,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC;AAAA,MACA,QAAQ,CAAC,MAAM,EAAE;AAAA,MACjB,aAAa,CAAC,MAAM,EAAE;AAAA,MACtB,aAAa,CAAC,IAAI,UAAU,QAAQ;AAAA,MACnC,GAAI,cAAc,SAAY,EAAE,UAAA,IAAc,CAAA;AAAA,MAC9C,GAAI,UAAU,SAAY,EAAE,MAAA,IAAU,CAAA;AAAA,MACtC,GAAI,iBAAiB,SAAY,EAAE,aAAA,IAAiB,CAAA;AAAA,MACpD,GAAI,eAAe,EAAE,cAAc,iBAAiB,CAAC,MAAS,EAAE,eAAe,KAAA,IAAS,CAAA;AAAA,MACzF,WAAW,CAAC,EAAE,MAAM,SAAS,YAC3B,qBAAC,OAAA,EAAI,WAAU,sEACZ,UAAA;AAAA,QAAA,cACC,YAAY,IAAI,IAEhB;AAAA,UAAC;AAAA,UAAA;AAAA,YACC;AAAA,YACA,UAAU,cAAc,QAAQ,KAAK,OAAO;AAAA,YAC3C,GAAI,eAAe,EAAE,UAAU,iBAAiB,CAAA;AAAA,UAAC;AAAA,QAAA;AAAA,QAGrD,WAAW,oBAAC,QAAA,EAAK,WAAU,kCAAiC,UAAA,KAAC;AAAA,QAC7D,SAAS,QACR,oBAAC,QAAA,EAAK,WAAU,0BACb,UAAA,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,EAAA,CACxD;AAAA,MAAA,EAAA,CAEJ;AAAA,IAAA;AAAA,EAAA;AAIR;AAEA,SAAS,mBAA8C;AAAA,EACrD;AAAA,EACA;AAAA,EACA,WAAW;AACb,GAIG;AACD,QAAM,QAAQ;AAAA;AAAA;AAAA;AAAA,IAIZ;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,MAAK;AAAA,QACL,SAAS,CAAC,MAAM;AACd,YAAE,gBAAA;AACF,mBAAS,IAAI;AAAA,QACf;AAAA,QACA,OAAM;AAAA,QACN,WAAW;AAAA,UACT;AAAA,UACA,YAAY;AAAA,QAAA;AAAA,QAGb,UAAA,KAAK;AAAA,MAAA;AAAA,IAAA;AAAA,MAGR,oBAAC,QAAA,EAAK,WAAU,yBAAyB,eAAK,OAAM;AAEtD,SACE,qBAAA,UAAA,EACG,UAAA;AAAA,IAAA;AAAA,IACA,KAAK,QAAQ,qBAAC,QAAA,EAAK,WAAU,4BAA2B,UAAA;AAAA,MAAA;AAAA,MAAE,KAAK;AAAA,IAAA,GAAK;AAAA,IACpE,KAAK,SAAS,OACb,oBAAC,UAAK,WAAU,6BAA6B,UAAA,OAAO,KAAK,KAAK,EAAA,CAAE,IAC9D,KAAK,OAAO,CAAC,KAAK,WACpB,oBAAC,UAAK,WAAU,6CAA6C,UAAA,KAAK,IAAA,CAAI,IACpE;AAAA,EAAA,GACN;AAEJ;"}
@@ -8,15 +8,20 @@ const status = require("./status.cjs");
8
8
  const filterState = require("./filterState.cjs");
9
9
  const UiAdd = require("../../icons/components/UiAdd.cjs");
10
10
  const UiRemove = require("../../icons/components/UiRemove.cjs");
11
- const STATUS_DEFS = [
12
- { key: "failed", label: "Failed", badge: "bg-red-500" },
13
- { key: "warned", label: "Warned", badge: "bg-amber-400" },
14
- { key: "timedout", label: "Timed out", badge: "bg-amber-500" },
15
- { key: "passed", label: "Passed", badge: "bg-green-500" },
16
- { key: "skipped", label: "Skipped", badge: "bg-yellow-400" },
17
- { key: "running", label: "Running", badge: "bg-blue-500" },
18
- { key: "pending", label: "Queued", badge: "bg-muted-foreground" }
19
- ];
11
+ const STATUS_BADGES = {
12
+ failed: "bg-red-500",
13
+ warned: "bg-amber-400",
14
+ timedout: "bg-amber-500",
15
+ passed: "bg-green-500",
16
+ skipped: "bg-yellow-400",
17
+ running: "bg-blue-500",
18
+ pending: "bg-muted-foreground",
19
+ total: "bg-muted-foreground"
20
+ };
21
+ const STATUS_DEFS = status.STATUS_LABELS.map((d) => ({
22
+ ...d,
23
+ badge: STATUS_BADGES[d.key]
24
+ }));
20
25
  function pillClass(mode) {
21
26
  if (mode === "include") return "bg-accent border-border font-medium text-foreground";
22
27
  if (mode === "exclude") return "bg-foreground text-background border-foreground font-medium";
@@ -1 +1 @@
1
- {"version":3,"file":"TestFilterBar.cjs","sources":["../../../src/data/test-runner/TestFilterBar.tsx"],"sourcesContent":["import { Icon } from \"../Icon\";\nimport { cn } from \"../../lib/utils\";\nimport { UiAdd, UiRemove } from \"../../icons\";\nimport { frameworkIcon } from \"./frameworkIcon\";\nimport { formatCount, type StatusCounts } from \"./status\";\nimport { cycleFilterState, type FilterMode, type TestFilters } from \"./filterState\";\n\nexport type TestFilterBarProps = {\n filters: TestFilters;\n onChange: (next: TestFilters) => void;\n counts: StatusCounts;\n frameworks: string[];\n className?: string;\n};\n\ntype StatusDef = { key: keyof StatusCounts; label: string; badge: string };\n\nconst STATUS_DEFS: StatusDef[] = [\n { key: \"failed\", label: \"Failed\", badge: \"bg-red-500\" },\n { key: \"warned\", label: \"Warned\", badge: \"bg-amber-400\" },\n { key: \"timedout\", label: \"Timed out\", badge: \"bg-amber-500\" },\n { key: \"passed\", label: \"Passed\", badge: \"bg-green-500\" },\n { key: \"skipped\", label: \"Skipped\", badge: \"bg-yellow-400\" },\n { key: \"running\", label: \"Running\", badge: \"bg-blue-500\" },\n { key: \"pending\", label: \"Queued\", badge: \"bg-muted-foreground\" },\n];\n\nfunction pillClass(mode: FilterMode | undefined): string {\n if (mode === \"include\") return \"bg-accent border-border font-medium text-foreground\";\n if (mode === \"exclude\") return \"bg-foreground text-background border-foreground font-medium\";\n return \"border-border text-muted-foreground hover:bg-accent\";\n}\n\nfunction StateMarker({ mode }: { mode: FilterMode | undefined }) {\n if (mode === \"include\") return <Icon icon={UiAdd} className=\"text-xs\" />;\n if (mode === \"exclude\") return <Icon icon={UiRemove} className=\"text-xs\" />;\n return <span className=\"h-2 w-2 rounded-full bg-current opacity-30\" aria-hidden />;\n}\n\n/**\n * Tri-state status + framework filter pills. Named TestFilterBar to avoid the\n * pre-existing generic `FilterBar` export in clicky-ui/components.\n */\nexport function TestFilterBar({\n filters,\n onChange,\n counts,\n frameworks,\n className,\n}: TestFilterBarProps) {\n const hasActive = filters.status.size > 0 || filters.framework.size > 0;\n\n return (\n <div className={cn(\"flex flex-wrap items-center gap-1.5\", className)}>\n {STATUS_DEFS.map((sf) => {\n const count = counts[sf.key];\n if (count === 0) return null;\n const mode = filters.status.get(sf.key);\n return (\n <button\n key={sf.key}\n type=\"button\"\n className={cn(\n \"inline-flex items-center gap-1.5 rounded-full border px-2 py-0.5 text-xs transition-colors\",\n pillClass(mode),\n )}\n onClick={() => onChange({ ...filters, status: cycleFilterState(filters.status, sf.key) })}\n title={`${sf.label}: ${mode ?? \"neutral\"}`}\n >\n <span\n className={cn(\n \"inline-flex h-4 min-w-4 items-center justify-center rounded-full px-1 text-[10px] font-bold text-white\",\n sf.badge,\n )}\n >\n {formatCount(count)}\n </span>\n <StateMarker mode={mode} />\n {sf.label}\n </button>\n );\n })}\n\n {frameworks.length > 1 && (\n <>\n <span className=\"mx-0.5 text-border\">|</span>\n {frameworks.map((fw) => {\n const icon = frameworkIcon(fw);\n const mode = filters.framework.get(fw);\n return (\n <button\n key={fw}\n type=\"button\"\n className={cn(\n \"inline-flex items-center gap-1 rounded-full border px-2 py-0.5 text-xs transition-colors\",\n pillClass(mode),\n )}\n onClick={() =>\n onChange({ ...filters, framework: cycleFilterState(filters.framework, fw) })\n }\n title={`${fw}: ${mode ?? \"neutral\"}`}\n >\n <StateMarker mode={mode} />\n {icon && <Icon icon={icon} className=\"text-sm\" />}\n {fw}\n </button>\n );\n })}\n </>\n )}\n\n {hasActive && (\n <button\n type=\"button\"\n className=\"ml-1 text-xs text-muted-foreground hover:text-foreground\"\n onClick={() => onChange({ status: new Map(), framework: new Map() })}\n >\n Clear\n </button>\n )}\n </div>\n );\n}\n"],"names":["jsx","Icon","UiAdd","UiRemove","cn","jsxs","cycleFilterState","Fragment","frameworkIcon"],"mappings":"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"}
1
+ {"version":3,"file":"TestFilterBar.cjs","sources":["../../../src/data/test-runner/TestFilterBar.tsx"],"sourcesContent":["import { Icon } from \"../Icon\";\nimport { cn } from \"../../lib/utils\";\nimport { UiAdd, UiRemove } from \"../../icons\";\nimport { frameworkIcon } from \"./frameworkIcon\";\nimport { formatCount, STATUS_LABELS, type StatusCounts } from \"./status\";\nimport { cycleFilterState, type FilterMode, type TestFilters } from \"./filterState\";\n\nexport type TestFilterBarProps = {\n filters: TestFilters;\n onChange: (next: TestFilters) => void;\n counts: StatusCounts;\n frameworks: string[];\n className?: string;\n};\n\ntype StatusDef = { key: keyof StatusCounts; label: string; badge: string };\n\nconst STATUS_BADGES: Record<keyof StatusCounts, string> = {\n failed: \"bg-red-500\",\n warned: \"bg-amber-400\",\n timedout: \"bg-amber-500\",\n passed: \"bg-green-500\",\n skipped: \"bg-yellow-400\",\n running: \"bg-blue-500\",\n pending: \"bg-muted-foreground\",\n total: \"bg-muted-foreground\",\n};\n\nconst STATUS_DEFS: StatusDef[] = STATUS_LABELS.map((d) => ({\n ...d,\n badge: STATUS_BADGES[d.key],\n}));\n\nfunction pillClass(mode: FilterMode | undefined): string {\n if (mode === \"include\") return \"bg-accent border-border font-medium text-foreground\";\n if (mode === \"exclude\") return \"bg-foreground text-background border-foreground font-medium\";\n return \"border-border text-muted-foreground hover:bg-accent\";\n}\n\nfunction StateMarker({ mode }: { mode: FilterMode | undefined }) {\n if (mode === \"include\") return <Icon icon={UiAdd} className=\"text-xs\" />;\n if (mode === \"exclude\") return <Icon icon={UiRemove} className=\"text-xs\" />;\n return <span className=\"h-2 w-2 rounded-full bg-current opacity-30\" aria-hidden />;\n}\n\n/**\n * Tri-state status + framework filter pills. Named TestFilterBar to avoid the\n * pre-existing generic `FilterBar` export in clicky-ui/components.\n */\nexport function TestFilterBar({\n filters,\n onChange,\n counts,\n frameworks,\n className,\n}: TestFilterBarProps) {\n const hasActive = filters.status.size > 0 || filters.framework.size > 0;\n\n return (\n <div className={cn(\"flex flex-wrap items-center gap-1.5\", className)}>\n {STATUS_DEFS.map((sf) => {\n const count = counts[sf.key];\n if (count === 0) return null;\n const mode = filters.status.get(sf.key);\n return (\n <button\n key={sf.key}\n type=\"button\"\n className={cn(\n \"inline-flex items-center gap-1.5 rounded-full border px-2 py-0.5 text-xs transition-colors\",\n pillClass(mode),\n )}\n onClick={() => onChange({ ...filters, status: cycleFilterState(filters.status, sf.key) })}\n title={`${sf.label}: ${mode ?? \"neutral\"}`}\n >\n <span\n className={cn(\n \"inline-flex h-4 min-w-4 items-center justify-center rounded-full px-1 text-[10px] font-bold text-white\",\n sf.badge,\n )}\n >\n {formatCount(count)}\n </span>\n <StateMarker mode={mode} />\n {sf.label}\n </button>\n );\n })}\n\n {frameworks.length > 1 && (\n <>\n <span className=\"mx-0.5 text-border\">|</span>\n {frameworks.map((fw) => {\n const icon = frameworkIcon(fw);\n const mode = filters.framework.get(fw);\n return (\n <button\n key={fw}\n type=\"button\"\n className={cn(\n \"inline-flex items-center gap-1 rounded-full border px-2 py-0.5 text-xs transition-colors\",\n pillClass(mode),\n )}\n onClick={() =>\n onChange({ ...filters, framework: cycleFilterState(filters.framework, fw) })\n }\n title={`${fw}: ${mode ?? \"neutral\"}`}\n >\n <StateMarker mode={mode} />\n {icon && <Icon icon={icon} className=\"text-sm\" />}\n {fw}\n </button>\n );\n })}\n </>\n )}\n\n {hasActive && (\n <button\n type=\"button\"\n className=\"ml-1 text-xs text-muted-foreground hover:text-foreground\"\n onClick={() => onChange({ status: new Map(), framework: new Map() })}\n >\n Clear\n </button>\n )}\n </div>\n );\n}\n"],"names":["STATUS_LABELS","jsx","Icon","UiAdd","UiRemove","cn","jsxs","cycleFilterState","Fragment","frameworkIcon"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"TestFilterBar.d.ts","sourceRoot":"","sources":["../../../src/data/test-runner/TestFilterBar.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAe,KAAK,YAAY,EAAE,MAAM,UAAU,CAAC;AAC1D,OAAO,EAAqC,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AAEpF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,OAAO,EAAE,WAAW,CAAC;IACrB,QAAQ,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,IAAI,CAAC;IACtC,MAAM,EAAE,YAAY,CAAC;IACrB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AA0BF;;;GAGG;AACH,wBAAgB,aAAa,CAAC,EAC5B,OAAO,EACP,QAAQ,EACR,MAAM,EACN,UAAU,EACV,SAAS,GACV,EAAE,kBAAkB,2CAyEpB"}
1
+ {"version":3,"file":"TestFilterBar.d.ts","sourceRoot":"","sources":["../../../src/data/test-runner/TestFilterBar.tsx"],"names":[],"mappings":"AAIA,OAAO,EAA8B,KAAK,YAAY,EAAE,MAAM,UAAU,CAAC;AACzE,OAAO,EAAqC,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AAEpF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,OAAO,EAAE,WAAW,CAAC;IACrB,QAAQ,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,IAAI,CAAC;IACtC,MAAM,EAAE,YAAY,CAAC;IACrB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAgCF;;;GAGG;AACH,wBAAgB,aAAa,CAAC,EAC5B,OAAO,EACP,QAAQ,EACR,MAAM,EACN,UAAU,EACV,SAAS,GACV,EAAE,kBAAkB,2CAyEpB"}
@@ -2,19 +2,24 @@ import { jsxs, jsx, Fragment } from "react/jsx-runtime";
2
2
  import { Icon } from "../Icon.js";
3
3
  import { cn } from "../../lib/utils.js";
4
4
  import { frameworkIcon } from "./frameworkIcon.js";
5
- import { formatCount } from "./status.js";
5
+ import { formatCount, STATUS_LABELS } from "./status.js";
6
6
  import { cycleFilterState } from "./filterState.js";
7
7
  import { UiAdd } from "../../icons/components/UiAdd.js";
8
8
  import { UiRemove } from "../../icons/components/UiRemove.js";
9
- const STATUS_DEFS = [
10
- { key: "failed", label: "Failed", badge: "bg-red-500" },
11
- { key: "warned", label: "Warned", badge: "bg-amber-400" },
12
- { key: "timedout", label: "Timed out", badge: "bg-amber-500" },
13
- { key: "passed", label: "Passed", badge: "bg-green-500" },
14
- { key: "skipped", label: "Skipped", badge: "bg-yellow-400" },
15
- { key: "running", label: "Running", badge: "bg-blue-500" },
16
- { key: "pending", label: "Queued", badge: "bg-muted-foreground" }
17
- ];
9
+ const STATUS_BADGES = {
10
+ failed: "bg-red-500",
11
+ warned: "bg-amber-400",
12
+ timedout: "bg-amber-500",
13
+ passed: "bg-green-500",
14
+ skipped: "bg-yellow-400",
15
+ running: "bg-blue-500",
16
+ pending: "bg-muted-foreground",
17
+ total: "bg-muted-foreground"
18
+ };
19
+ const STATUS_DEFS = STATUS_LABELS.map((d) => ({
20
+ ...d,
21
+ badge: STATUS_BADGES[d.key]
22
+ }));
18
23
  function pillClass(mode) {
19
24
  if (mode === "include") return "bg-accent border-border font-medium text-foreground";
20
25
  if (mode === "exclude") return "bg-foreground text-background border-foreground font-medium";
@@ -1 +1 @@
1
- {"version":3,"file":"TestFilterBar.js","sources":["../../../src/data/test-runner/TestFilterBar.tsx"],"sourcesContent":["import { Icon } from \"../Icon\";\nimport { cn } from \"../../lib/utils\";\nimport { UiAdd, UiRemove } from \"../../icons\";\nimport { frameworkIcon } from \"./frameworkIcon\";\nimport { formatCount, type StatusCounts } from \"./status\";\nimport { cycleFilterState, type FilterMode, type TestFilters } from \"./filterState\";\n\nexport type TestFilterBarProps = {\n filters: TestFilters;\n onChange: (next: TestFilters) => void;\n counts: StatusCounts;\n frameworks: string[];\n className?: string;\n};\n\ntype StatusDef = { key: keyof StatusCounts; label: string; badge: string };\n\nconst STATUS_DEFS: StatusDef[] = [\n { key: \"failed\", label: \"Failed\", badge: \"bg-red-500\" },\n { key: \"warned\", label: \"Warned\", badge: \"bg-amber-400\" },\n { key: \"timedout\", label: \"Timed out\", badge: \"bg-amber-500\" },\n { key: \"passed\", label: \"Passed\", badge: \"bg-green-500\" },\n { key: \"skipped\", label: \"Skipped\", badge: \"bg-yellow-400\" },\n { key: \"running\", label: \"Running\", badge: \"bg-blue-500\" },\n { key: \"pending\", label: \"Queued\", badge: \"bg-muted-foreground\" },\n];\n\nfunction pillClass(mode: FilterMode | undefined): string {\n if (mode === \"include\") return \"bg-accent border-border font-medium text-foreground\";\n if (mode === \"exclude\") return \"bg-foreground text-background border-foreground font-medium\";\n return \"border-border text-muted-foreground hover:bg-accent\";\n}\n\nfunction StateMarker({ mode }: { mode: FilterMode | undefined }) {\n if (mode === \"include\") return <Icon icon={UiAdd} className=\"text-xs\" />;\n if (mode === \"exclude\") return <Icon icon={UiRemove} className=\"text-xs\" />;\n return <span className=\"h-2 w-2 rounded-full bg-current opacity-30\" aria-hidden />;\n}\n\n/**\n * Tri-state status + framework filter pills. Named TestFilterBar to avoid the\n * pre-existing generic `FilterBar` export in clicky-ui/components.\n */\nexport function TestFilterBar({\n filters,\n onChange,\n counts,\n frameworks,\n className,\n}: TestFilterBarProps) {\n const hasActive = filters.status.size > 0 || filters.framework.size > 0;\n\n return (\n <div className={cn(\"flex flex-wrap items-center gap-1.5\", className)}>\n {STATUS_DEFS.map((sf) => {\n const count = counts[sf.key];\n if (count === 0) return null;\n const mode = filters.status.get(sf.key);\n return (\n <button\n key={sf.key}\n type=\"button\"\n className={cn(\n \"inline-flex items-center gap-1.5 rounded-full border px-2 py-0.5 text-xs transition-colors\",\n pillClass(mode),\n )}\n onClick={() => onChange({ ...filters, status: cycleFilterState(filters.status, sf.key) })}\n title={`${sf.label}: ${mode ?? \"neutral\"}`}\n >\n <span\n className={cn(\n \"inline-flex h-4 min-w-4 items-center justify-center rounded-full px-1 text-[10px] font-bold text-white\",\n sf.badge,\n )}\n >\n {formatCount(count)}\n </span>\n <StateMarker mode={mode} />\n {sf.label}\n </button>\n );\n })}\n\n {frameworks.length > 1 && (\n <>\n <span className=\"mx-0.5 text-border\">|</span>\n {frameworks.map((fw) => {\n const icon = frameworkIcon(fw);\n const mode = filters.framework.get(fw);\n return (\n <button\n key={fw}\n type=\"button\"\n className={cn(\n \"inline-flex items-center gap-1 rounded-full border px-2 py-0.5 text-xs transition-colors\",\n pillClass(mode),\n )}\n onClick={() =>\n onChange({ ...filters, framework: cycleFilterState(filters.framework, fw) })\n }\n title={`${fw}: ${mode ?? \"neutral\"}`}\n >\n <StateMarker mode={mode} />\n {icon && <Icon icon={icon} className=\"text-sm\" />}\n {fw}\n </button>\n );\n })}\n </>\n )}\n\n {hasActive && (\n <button\n type=\"button\"\n className=\"ml-1 text-xs text-muted-foreground hover:text-foreground\"\n onClick={() => onChange({ status: new Map(), framework: new Map() })}\n >\n Clear\n </button>\n )}\n </div>\n );\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"TestFilterBar.js","sources":["../../../src/data/test-runner/TestFilterBar.tsx"],"sourcesContent":["import { Icon } from \"../Icon\";\nimport { cn } from \"../../lib/utils\";\nimport { UiAdd, UiRemove } from \"../../icons\";\nimport { frameworkIcon } from \"./frameworkIcon\";\nimport { formatCount, STATUS_LABELS, type StatusCounts } from \"./status\";\nimport { cycleFilterState, type FilterMode, type TestFilters } from \"./filterState\";\n\nexport type TestFilterBarProps = {\n filters: TestFilters;\n onChange: (next: TestFilters) => void;\n counts: StatusCounts;\n frameworks: string[];\n className?: string;\n};\n\ntype StatusDef = { key: keyof StatusCounts; label: string; badge: string };\n\nconst STATUS_BADGES: Record<keyof StatusCounts, string> = {\n failed: \"bg-red-500\",\n warned: \"bg-amber-400\",\n timedout: \"bg-amber-500\",\n passed: \"bg-green-500\",\n skipped: \"bg-yellow-400\",\n running: \"bg-blue-500\",\n pending: \"bg-muted-foreground\",\n total: \"bg-muted-foreground\",\n};\n\nconst STATUS_DEFS: StatusDef[] = STATUS_LABELS.map((d) => ({\n ...d,\n badge: STATUS_BADGES[d.key],\n}));\n\nfunction pillClass(mode: FilterMode | undefined): string {\n if (mode === \"include\") return \"bg-accent border-border font-medium text-foreground\";\n if (mode === \"exclude\") return \"bg-foreground text-background border-foreground font-medium\";\n return \"border-border text-muted-foreground hover:bg-accent\";\n}\n\nfunction StateMarker({ mode }: { mode: FilterMode | undefined }) {\n if (mode === \"include\") return <Icon icon={UiAdd} className=\"text-xs\" />;\n if (mode === \"exclude\") return <Icon icon={UiRemove} className=\"text-xs\" />;\n return <span className=\"h-2 w-2 rounded-full bg-current opacity-30\" aria-hidden />;\n}\n\n/**\n * Tri-state status + framework filter pills. Named TestFilterBar to avoid the\n * pre-existing generic `FilterBar` export in clicky-ui/components.\n */\nexport function TestFilterBar({\n filters,\n onChange,\n counts,\n frameworks,\n className,\n}: TestFilterBarProps) {\n const hasActive = filters.status.size > 0 || filters.framework.size > 0;\n\n return (\n <div className={cn(\"flex flex-wrap items-center gap-1.5\", className)}>\n {STATUS_DEFS.map((sf) => {\n const count = counts[sf.key];\n if (count === 0) return null;\n const mode = filters.status.get(sf.key);\n return (\n <button\n key={sf.key}\n type=\"button\"\n className={cn(\n \"inline-flex items-center gap-1.5 rounded-full border px-2 py-0.5 text-xs transition-colors\",\n pillClass(mode),\n )}\n onClick={() => onChange({ ...filters, status: cycleFilterState(filters.status, sf.key) })}\n title={`${sf.label}: ${mode ?? \"neutral\"}`}\n >\n <span\n className={cn(\n \"inline-flex h-4 min-w-4 items-center justify-center rounded-full px-1 text-[10px] font-bold text-white\",\n sf.badge,\n )}\n >\n {formatCount(count)}\n </span>\n <StateMarker mode={mode} />\n {sf.label}\n </button>\n );\n })}\n\n {frameworks.length > 1 && (\n <>\n <span className=\"mx-0.5 text-border\">|</span>\n {frameworks.map((fw) => {\n const icon = frameworkIcon(fw);\n const mode = filters.framework.get(fw);\n return (\n <button\n key={fw}\n type=\"button\"\n className={cn(\n \"inline-flex items-center gap-1 rounded-full border px-2 py-0.5 text-xs transition-colors\",\n pillClass(mode),\n )}\n onClick={() =>\n onChange({ ...filters, framework: cycleFilterState(filters.framework, fw) })\n }\n title={`${fw}: ${mode ?? \"neutral\"}`}\n >\n <StateMarker mode={mode} />\n {icon && <Icon icon={icon} className=\"text-sm\" />}\n {fw}\n </button>\n );\n })}\n </>\n )}\n\n {hasActive && (\n <button\n type=\"button\"\n className=\"ml-1 text-xs text-muted-foreground hover:text-foreground\"\n onClick={() => onChange({ status: new Map(), framework: new Map() })}\n >\n Clear\n </button>\n )}\n </div>\n );\n}\n"],"names":[],"mappings":";;;;;;;;AAiBA,MAAM,gBAAoD;AAAA,EACxD,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,SAAS;AAAA,EACT,SAAS;AAAA,EACT,OAAO;AACT;AAEA,MAAM,cAA2B,cAAc,IAAI,CAAC,OAAO;AAAA,EACzD,GAAG;AAAA,EACH,OAAO,cAAc,EAAE,GAAG;AAC5B,EAAE;AAEF,SAAS,UAAU,MAAsC;AACvD,MAAI,SAAS,UAAW,QAAO;AAC/B,MAAI,SAAS,UAAW,QAAO;AAC/B,SAAO;AACT;AAEA,SAAS,YAAY,EAAE,QAA0C;AAC/D,MAAI,SAAS,UAAW,QAAO,oBAAC,QAAK,MAAM,OAAO,WAAU,WAAU;AACtE,MAAI,SAAS,UAAW,QAAO,oBAAC,QAAK,MAAM,UAAU,WAAU,WAAU;AACzE,SAAO,oBAAC,QAAA,EAAK,WAAU,8CAA6C,eAAW,MAAC;AAClF;AAMO,SAAS,cAAc;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAuB;AACrB,QAAM,YAAY,QAAQ,OAAO,OAAO,KAAK,QAAQ,UAAU,OAAO;AAEtE,8BACG,OAAA,EAAI,WAAW,GAAG,uCAAuC,SAAS,GAChE,UAAA;AAAA,IAAA,YAAY,IAAI,CAAC,OAAO;AACvB,YAAM,QAAQ,OAAO,GAAG,GAAG;AAC3B,UAAI,UAAU,EAAG,QAAO;AACxB,YAAM,OAAO,QAAQ,OAAO,IAAI,GAAG,GAAG;AACtC,aACE;AAAA,QAAC;AAAA,QAAA;AAAA,UAEC,MAAK;AAAA,UACL,WAAW;AAAA,YACT;AAAA,YACA,UAAU,IAAI;AAAA,UAAA;AAAA,UAEhB,SAAS,MAAM,SAAS,EAAE,GAAG,SAAS,QAAQ,iBAAiB,QAAQ,QAAQ,GAAG,GAAG,GAAG;AAAA,UACxF,OAAO,GAAG,GAAG,KAAK,KAAK,QAAQ,SAAS;AAAA,UAExC,UAAA;AAAA,YAAA;AAAA,cAAC;AAAA,cAAA;AAAA,gBACC,WAAW;AAAA,kBACT;AAAA,kBACA,GAAG;AAAA,gBAAA;AAAA,gBAGJ,sBAAY,KAAK;AAAA,cAAA;AAAA,YAAA;AAAA,YAEpB,oBAAC,eAAY,MAAY;AAAA,YACxB,GAAG;AAAA,UAAA;AAAA,QAAA;AAAA,QAlBC,GAAG;AAAA,MAAA;AAAA,IAqBd,CAAC;AAAA,IAEA,WAAW,SAAS,KACnB,qBAAA,UAAA,EACE,UAAA;AAAA,MAAA,oBAAC,QAAA,EAAK,WAAU,sBAAqB,UAAA,KAAC;AAAA,MACrC,WAAW,IAAI,CAAC,OAAO;AACtB,cAAM,OAAO,cAAc,EAAE;AAC7B,cAAM,OAAO,QAAQ,UAAU,IAAI,EAAE;AACrC,eACE;AAAA,UAAC;AAAA,UAAA;AAAA,YAEC,MAAK;AAAA,YACL,WAAW;AAAA,cACT;AAAA,cACA,UAAU,IAAI;AAAA,YAAA;AAAA,YAEhB,SAAS,MACP,SAAS,EAAE,GAAG,SAAS,WAAW,iBAAiB,QAAQ,WAAW,EAAE,GAAG;AAAA,YAE7E,OAAO,GAAG,EAAE,KAAK,QAAQ,SAAS;AAAA,YAElC,UAAA;AAAA,cAAA,oBAAC,eAAY,MAAY;AAAA,cACxB,QAAQ,oBAAC,MAAA,EAAK,MAAY,WAAU,WAAU;AAAA,cAC9C;AAAA,YAAA;AAAA,UAAA;AAAA,UAbI;AAAA,QAAA;AAAA,MAgBX,CAAC;AAAA,IAAA,GACH;AAAA,IAGD,aACC;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,MAAK;AAAA,QACL,WAAU;AAAA,QACV,SAAS,MAAM,SAAS,EAAE,QAAQ,oBAAI,IAAA,GAAO,WAAW,oBAAI,IAAA,GAAO;AAAA,QACpE,UAAA;AAAA,MAAA;AAAA,IAAA;AAAA,EAED,GAEJ;AAEJ;"}
@@ -8,16 +8,6 @@ const status = require("./status.cjs");
8
8
  const UiClock = require("../../icons/components/UiClock.cjs");
9
9
  const UiCheck = require("../../icons/components/UiCheck.cjs");
10
10
  const UiLoader = require("../../icons/components/UiLoader.cjs");
11
- function totals(tests) {
12
- return tests.reduce(
13
- (acc, t) => {
14
- const s = status.sumNonTaskTests(t);
15
- Object.keys(acc).forEach((k) => acc[k] += s[k]);
16
- return acc;
17
- },
18
- { total: 0, passed: 0, failed: 0, warned: 0, skipped: 0, pending: 0, running: 0, timedout: 0 }
19
- );
20
- }
21
11
  function pct(part, total) {
22
12
  return total > 0 ? Math.round(part / total * 100) : 0;
23
13
  }
@@ -31,7 +21,7 @@ function TestRunSummary({
31
21
  compact = false,
32
22
  className
33
23
  }) {
34
- const t = totals(tests);
24
+ const t = status.aggregateStatusCounts(tests);
35
25
  const clock = now ?? 0;
36
26
  const end = done && endTime ? endTime : clock;
37
27
  const elapsed = startTime && end >= startTime ? `${((end - startTime) / 1e3).toFixed(1)}s` : "";
@@ -1 +1 @@
1
- {"version":3,"file":"TestRunSummary.cjs","sources":["../../../src/data/test-runner/TestRunSummary.tsx"],"sourcesContent":["import { Icon } from \"../Icon\";\nimport { ProgressBar, type ProgressSegment } from \"../ProgressBar\";\nimport { cn } from \"../../lib/utils\";\nimport { UiCheck, UiClock, UiLoader } from \"../../icons\";\nimport type { RunMeta, Test } from \"./types\";\nimport { sumNonTaskTests, type StatusCounts } from \"./status\";\n\nexport type TestRunSummaryProps = {\n tests: Test[];\n /** Run start, epoch ms (host-provided). */\n startTime?: number | null | undefined;\n /** Run end, epoch ms; falls back to `now` while running. */\n endTime?: number | null | undefined;\n done: boolean;\n /** Injected clock (epoch ms) so the component stays pure. */\n now?: number | undefined;\n runMeta?: RunMeta | undefined;\n /**\n * Single-line layout for embedding in a host header (e.g. a dialog header):\n * counts + a short inline progress bar on one row, instead of the default\n * two-line right-aligned column.\n */\n compact?: boolean | undefined;\n className?: string | undefined;\n};\n\nfunction totals(tests: Test[]): StatusCounts {\n return tests.reduce(\n (acc, t) => {\n const s = sumNonTaskTests(t);\n (Object.keys(acc) as (keyof StatusCounts)[]).forEach((k) => (acc[k] += s[k]));\n return acc;\n },\n { total: 0, passed: 0, failed: 0, warned: 0, skipped: 0, pending: 0, running: 0, timedout: 0 },\n );\n}\n\nfunction pct(part: number, total: number): number {\n return total > 0 ? Math.round((part / total) * 100) : 0;\n}\n\n/**\n * Header summary: per-status counts, elapsed time, a stacked progress bar, and\n * pass/fail/pending gauge cards. Pure — elapsed time comes from injected `now`.\n */\nexport function TestRunSummary({\n tests,\n startTime,\n endTime,\n done,\n now,\n runMeta,\n compact = false,\n className,\n}: TestRunSummaryProps) {\n const t = totals(tests);\n const clock = now ?? 0;\n const end = done && endTime ? endTime : clock;\n const elapsed = startTime && end >= startTime ? `${((end - startTime) / 1000).toFixed(1)}s` : \"\";\n\n const segments: ProgressSegment[] = [\n { count: t.passed, color: \"bg-green-500\", label: \"passed\" },\n { count: t.skipped, color: \"bg-yellow-400\", label: \"skipped\" },\n { count: t.failed, color: \"bg-red-500\", label: \"failed\" },\n { count: t.timedout, color: \"bg-amber-500\", label: \"timed out\" },\n { count: t.running, color: \"bg-blue-400\", label: \"running\" },\n { count: t.pending, color: \"bg-muted-foreground/40\", label: \"queued\" },\n ];\n\n // Each status appears once, as a coloured count chip — no separate gauge-card\n // row repeating the same numbers as percentages.\n const chips: { n: number; label: string; tone: string }[] = [\n { n: t.passed, label: \"passed\", tone: \"text-green-600 dark:text-green-400\" },\n { n: t.failed, label: \"failed\", tone: \"text-red-600 dark:text-red-400\" },\n { n: t.timedout, label: \"timed out\", tone: \"text-amber-600 dark:text-amber-400\" },\n { n: t.skipped, label: \"skipped\", tone: \"text-yellow-700 dark:text-yellow-500\" },\n { n: t.running, label: \"running\", tone: \"text-blue-600 dark:text-blue-400\" },\n { n: t.pending, label: \"queued\", tone: \"text-muted-foreground\" },\n ];\n\n if (compact) {\n return (\n <div\n className={cn(\n \"flex flex-wrap items-center gap-x-3 gap-y-0.5 text-sm text-muted-foreground\",\n className,\n )}\n >\n <span className=\"font-medium text-foreground\">\n {t.total} tests\n {runMeta?.kind === \"rerun\" ? ` · rerun #${runMeta.sequence}` : \"\"}\n </span>\n {chips\n .filter((c) => c.n > 0)\n .map((c) => (\n <span key={c.label} className={c.tone}>\n {c.n} {c.label}\n </span>\n ))}\n {elapsed && (\n <span className=\"inline-flex items-center gap-1\">\n <Icon icon={UiClock} className=\"text-xs\" />\n {elapsed}\n </span>\n )}\n {t.total > 0 && (\n <span className=\"inline-flex items-center gap-1.5\">\n <ProgressBar className=\"w-24\" segments={segments} total={t.total} height=\"h-1.5\" />\n <span className=\"text-xs font-medium text-green-600 dark:text-green-400\">\n {pct(t.passed, t.total)}%\n </span>\n </span>\n )}\n {done ? (\n <Icon icon={UiCheck} className=\"text-green-600 dark:text-green-400\" />\n ) : (\n <Icon icon={UiLoader} className=\"animate-spin text-blue-500\" />\n )}\n </div>\n );\n }\n\n return (\n <div className={cn(\"flex min-w-[18rem] flex-col items-end gap-1.5\", className)}>\n <div className=\"flex flex-wrap items-center justify-end gap-x-3 gap-y-0.5 text-sm text-muted-foreground\">\n <span className=\"font-medium text-foreground\">\n {t.total} tests\n {runMeta?.kind === \"rerun\" ? ` · rerun #${runMeta.sequence}` : \"\"}\n </span>\n {chips\n .filter((c) => c.n > 0)\n .map((c) => (\n <span key={c.label} className={c.tone}>\n {c.n} {c.label}\n </span>\n ))}\n {elapsed && (\n <span className=\"inline-flex items-center gap-1\">\n <Icon icon={UiClock} className=\"text-xs\" />\n {elapsed}\n </span>\n )}\n {done ? (\n <Icon icon={UiCheck} className=\"text-green-600 dark:text-green-400\" />\n ) : (\n <Icon icon={UiLoader} className=\"animate-spin text-blue-500\" />\n )}\n </div>\n\n {t.total > 0 && (\n <div className=\"flex w-72 items-center gap-2\">\n <ProgressBar className=\"flex-1\" segments={segments} total={t.total} height=\"h-2\" />\n <span className=\"shrink-0 text-xs font-medium text-green-600 dark:text-green-400\">\n {pct(t.passed, t.total)}%\n </span>\n </div>\n )}\n </div>\n );\n}\n"],"names":["sumNonTaskTests","jsxs","cn","jsx","Icon","UiClock","ProgressBar","UiCheck","UiLoader"],"mappings":";;;;;;;;;;AA0BA,SAAS,OAAO,OAA6B;AAC3C,SAAO,MAAM;AAAA,IACX,CAAC,KAAK,MAAM;AACV,YAAM,IAAIA,OAAAA,gBAAgB,CAAC;AAC1B,aAAO,KAAK,GAAG,EAA6B,QAAQ,CAAC,MAAO,IAAI,CAAC,KAAK,EAAE,CAAC,CAAE;AAC5E,aAAO;AAAA,IACT;AAAA,IACA,EAAE,OAAO,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,UAAU,EAAA;AAAA,EAAE;AAEjG;AAEA,SAAS,IAAI,MAAc,OAAuB;AAChD,SAAO,QAAQ,IAAI,KAAK,MAAO,OAAO,QAAS,GAAG,IAAI;AACxD;AAMO,SAAS,eAAe;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV;AACF,GAAwB;AACtB,QAAM,IAAI,OAAO,KAAK;AACtB,QAAM,QAAQ,OAAO;AACrB,QAAM,MAAM,QAAQ,UAAU,UAAU;AACxC,QAAM,UAAU,aAAa,OAAO,YAAY,KAAK,MAAM,aAAa,KAAM,QAAQ,CAAC,CAAC,MAAM;AAE9F,QAAM,WAA8B;AAAA,IAClC,EAAE,OAAO,EAAE,QAAQ,OAAO,gBAAgB,OAAO,SAAA;AAAA,IACjD,EAAE,OAAO,EAAE,SAAS,OAAO,iBAAiB,OAAO,UAAA;AAAA,IACnD,EAAE,OAAO,EAAE,QAAQ,OAAO,cAAc,OAAO,SAAA;AAAA,IAC/C,EAAE,OAAO,EAAE,UAAU,OAAO,gBAAgB,OAAO,YAAA;AAAA,IACnD,EAAE,OAAO,EAAE,SAAS,OAAO,eAAe,OAAO,UAAA;AAAA,IACjD,EAAE,OAAO,EAAE,SAAS,OAAO,0BAA0B,OAAO,SAAA;AAAA,EAAS;AAKvE,QAAM,QAAsD;AAAA,IAC1D,EAAE,GAAG,EAAE,QAAQ,OAAO,UAAU,MAAM,qCAAA;AAAA,IACtC,EAAE,GAAG,EAAE,QAAQ,OAAO,UAAU,MAAM,iCAAA;AAAA,IACtC,EAAE,GAAG,EAAE,UAAU,OAAO,aAAa,MAAM,qCAAA;AAAA,IAC3C,EAAE,GAAG,EAAE,SAAS,OAAO,WAAW,MAAM,uCAAA;AAAA,IACxC,EAAE,GAAG,EAAE,SAAS,OAAO,WAAW,MAAM,mCAAA;AAAA,IACxC,EAAE,GAAG,EAAE,SAAS,OAAO,UAAU,MAAM,wBAAA;AAAA,EAAwB;AAGjE,MAAI,SAAS;AACX,WACEC,2BAAAA;AAAAA,MAAC;AAAA,MAAA;AAAA,QACC,WAAWC,MAAAA;AAAAA,UACT;AAAA,UACA;AAAA,QAAA;AAAA,QAGF,UAAA;AAAA,UAAAD,2BAAAA,KAAC,QAAA,EAAK,WAAU,+BACb,UAAA;AAAA,YAAA,EAAE;AAAA,YAAM;AAAA,aACR,mCAAS,UAAS,UAAU,aAAa,QAAQ,QAAQ,KAAK;AAAA,UAAA,GACjE;AAAA,UACC,MACE,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,EACrB,IAAI,CAAC,MACJA,2BAAAA,KAAC,QAAA,EAAmB,WAAW,EAAE,MAC9B,UAAA;AAAA,YAAA,EAAE;AAAA,YAAE;AAAA,YAAE,EAAE;AAAA,UAAA,KADA,EAAE,KAEb,CACD;AAAA,UACF,WACCA,2BAAAA,KAAC,QAAA,EAAK,WAAU,kCACd,UAAA;AAAA,YAAAE,2BAAAA,IAACC,KAAAA,MAAA,EAAK,MAAMC,QAAAA,SAAS,WAAU,WAAU;AAAA,YACxC;AAAA,UAAA,GACH;AAAA,UAED,EAAE,QAAQ,KACTJ,2BAAAA,KAAC,QAAA,EAAK,WAAU,oCACd,UAAA;AAAA,YAAAE,2BAAAA,IAACG,YAAAA,aAAA,EAAY,WAAU,QAAO,UAAoB,OAAO,EAAE,OAAO,QAAO,QAAA,CAAQ;AAAA,YACjFL,2BAAAA,KAAC,QAAA,EAAK,WAAU,0DACb,UAAA;AAAA,cAAA,IAAI,EAAE,QAAQ,EAAE,KAAK;AAAA,cAAE;AAAA,YAAA,EAAA,CAC1B;AAAA,UAAA,GACF;AAAA,UAED,OACCE,2BAAAA,IAACC,KAAAA,MAAA,EAAK,MAAMG,QAAAA,SAAS,WAAU,qCAAA,CAAqC,IAEpEJ,2BAAAA,IAACC,KAAAA,MAAA,EAAK,MAAMI,SAAAA,UAAU,WAAU,6BAAA,CAA6B;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAAA,EAIrE;AAEA,yCACG,OAAA,EAAI,WAAWN,MAAAA,GAAG,iDAAiD,SAAS,GAC3E,UAAA;AAAA,IAAAD,2BAAAA,KAAC,OAAA,EAAI,WAAU,2FACb,UAAA;AAAA,MAAAA,2BAAAA,KAAC,QAAA,EAAK,WAAU,+BACb,UAAA;AAAA,QAAA,EAAE;AAAA,QAAM;AAAA,SACR,mCAAS,UAAS,UAAU,aAAa,QAAQ,QAAQ,KAAK;AAAA,MAAA,GACjE;AAAA,MACC,MACE,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,EACrB,IAAI,CAAC,MACJA,2BAAAA,KAAC,QAAA,EAAmB,WAAW,EAAE,MAC9B,UAAA;AAAA,QAAA,EAAE;AAAA,QAAE;AAAA,QAAE,EAAE;AAAA,MAAA,KADA,EAAE,KAEb,CACD;AAAA,MACF,WACCA,2BAAAA,KAAC,QAAA,EAAK,WAAU,kCACd,UAAA;AAAA,QAAAE,2BAAAA,IAACC,KAAAA,MAAA,EAAK,MAAMC,QAAAA,SAAS,WAAU,WAAU;AAAA,QACxC;AAAA,MAAA,GACH;AAAA,MAED,OACCF,2BAAAA,IAACC,KAAAA,MAAA,EAAK,MAAMG,QAAAA,SAAS,WAAU,qCAAA,CAAqC,IAEpEJ,2BAAAA,IAACC,KAAAA,MAAA,EAAK,MAAMI,SAAAA,UAAU,WAAU,6BAAA,CAA6B;AAAA,IAAA,GAEjE;AAAA,IAEC,EAAE,QAAQ,KACTP,2BAAAA,KAAC,OAAA,EAAI,WAAU,gCACb,UAAA;AAAA,MAAAE,2BAAAA,IAACG,YAAAA,aAAA,EAAY,WAAU,UAAS,UAAoB,OAAO,EAAE,OAAO,QAAO,MAAA,CAAM;AAAA,MACjFL,2BAAAA,KAAC,QAAA,EAAK,WAAU,mEACb,UAAA;AAAA,QAAA,IAAI,EAAE,QAAQ,EAAE,KAAK;AAAA,QAAE;AAAA,MAAA,EAAA,CAC1B;AAAA,IAAA,EAAA,CACF;AAAA,EAAA,GAEJ;AAEJ;;"}
1
+ {"version":3,"file":"TestRunSummary.cjs","sources":["../../../src/data/test-runner/TestRunSummary.tsx"],"sourcesContent":["import { Icon } from \"../Icon\";\nimport { ProgressBar, type ProgressSegment } from \"../ProgressBar\";\nimport { cn } from \"../../lib/utils\";\nimport { UiCheck, UiClock, UiLoader } from \"../../icons\";\nimport type { RunMeta, Test } from \"./types\";\nimport { aggregateStatusCounts } from \"./status\";\n\nexport type TestRunSummaryProps = {\n tests: Test[];\n /** Run start, epoch ms (host-provided). */\n startTime?: number | null | undefined;\n /** Run end, epoch ms; falls back to `now` while running. */\n endTime?: number | null | undefined;\n done: boolean;\n /** Injected clock (epoch ms) so the component stays pure. */\n now?: number | undefined;\n runMeta?: RunMeta | undefined;\n /**\n * Single-line layout for embedding in a host header (e.g. a dialog header):\n * counts + a short inline progress bar on one row, instead of the default\n * two-line right-aligned column.\n */\n compact?: boolean | undefined;\n className?: string | undefined;\n};\n\nfunction pct(part: number, total: number): number {\n return total > 0 ? Math.round((part / total) * 100) : 0;\n}\n\n/**\n * Header summary: per-status counts, elapsed time, a stacked progress bar, and\n * pass/fail/pending gauge cards. Pure — elapsed time comes from injected `now`.\n */\nexport function TestRunSummary({\n tests,\n startTime,\n endTime,\n done,\n now,\n runMeta,\n compact = false,\n className,\n}: TestRunSummaryProps) {\n const t = aggregateStatusCounts(tests);\n const clock = now ?? 0;\n const end = done && endTime ? endTime : clock;\n const elapsed = startTime && end >= startTime ? `${((end - startTime) / 1000).toFixed(1)}s` : \"\";\n\n const segments: ProgressSegment[] = [\n { count: t.passed, color: \"bg-green-500\", label: \"passed\" },\n { count: t.skipped, color: \"bg-yellow-400\", label: \"skipped\" },\n { count: t.failed, color: \"bg-red-500\", label: \"failed\" },\n { count: t.timedout, color: \"bg-amber-500\", label: \"timed out\" },\n { count: t.running, color: \"bg-blue-400\", label: \"running\" },\n { count: t.pending, color: \"bg-muted-foreground/40\", label: \"queued\" },\n ];\n\n // Each status appears once, as a coloured count chip — no separate gauge-card\n // row repeating the same numbers as percentages.\n const chips: { n: number; label: string; tone: string }[] = [\n { n: t.passed, label: \"passed\", tone: \"text-green-600 dark:text-green-400\" },\n { n: t.failed, label: \"failed\", tone: \"text-red-600 dark:text-red-400\" },\n { n: t.timedout, label: \"timed out\", tone: \"text-amber-600 dark:text-amber-400\" },\n { n: t.skipped, label: \"skipped\", tone: \"text-yellow-700 dark:text-yellow-500\" },\n { n: t.running, label: \"running\", tone: \"text-blue-600 dark:text-blue-400\" },\n { n: t.pending, label: \"queued\", tone: \"text-muted-foreground\" },\n ];\n\n if (compact) {\n return (\n <div\n className={cn(\n \"flex flex-wrap items-center gap-x-3 gap-y-0.5 text-sm text-muted-foreground\",\n className,\n )}\n >\n <span className=\"font-medium text-foreground\">\n {t.total} tests\n {runMeta?.kind === \"rerun\" ? ` · rerun #${runMeta.sequence}` : \"\"}\n </span>\n {chips\n .filter((c) => c.n > 0)\n .map((c) => (\n <span key={c.label} className={c.tone}>\n {c.n} {c.label}\n </span>\n ))}\n {elapsed && (\n <span className=\"inline-flex items-center gap-1\">\n <Icon icon={UiClock} className=\"text-xs\" />\n {elapsed}\n </span>\n )}\n {t.total > 0 && (\n <span className=\"inline-flex items-center gap-1.5\">\n <ProgressBar className=\"w-24\" segments={segments} total={t.total} height=\"h-1.5\" />\n <span className=\"text-xs font-medium text-green-600 dark:text-green-400\">\n {pct(t.passed, t.total)}%\n </span>\n </span>\n )}\n {done ? (\n <Icon icon={UiCheck} className=\"text-green-600 dark:text-green-400\" />\n ) : (\n <Icon icon={UiLoader} className=\"animate-spin text-blue-500\" />\n )}\n </div>\n );\n }\n\n return (\n <div className={cn(\"flex min-w-[18rem] flex-col items-end gap-1.5\", className)}>\n <div className=\"flex flex-wrap items-center justify-end gap-x-3 gap-y-0.5 text-sm text-muted-foreground\">\n <span className=\"font-medium text-foreground\">\n {t.total} tests\n {runMeta?.kind === \"rerun\" ? ` · rerun #${runMeta.sequence}` : \"\"}\n </span>\n {chips\n .filter((c) => c.n > 0)\n .map((c) => (\n <span key={c.label} className={c.tone}>\n {c.n} {c.label}\n </span>\n ))}\n {elapsed && (\n <span className=\"inline-flex items-center gap-1\">\n <Icon icon={UiClock} className=\"text-xs\" />\n {elapsed}\n </span>\n )}\n {done ? (\n <Icon icon={UiCheck} className=\"text-green-600 dark:text-green-400\" />\n ) : (\n <Icon icon={UiLoader} className=\"animate-spin text-blue-500\" />\n )}\n </div>\n\n {t.total > 0 && (\n <div className=\"flex w-72 items-center gap-2\">\n <ProgressBar className=\"flex-1\" segments={segments} total={t.total} height=\"h-2\" />\n <span className=\"shrink-0 text-xs font-medium text-green-600 dark:text-green-400\">\n {pct(t.passed, t.total)}%\n </span>\n </div>\n )}\n </div>\n );\n}\n"],"names":["aggregateStatusCounts","jsxs","cn","jsx","Icon","UiClock","ProgressBar","UiCheck","UiLoader"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"TestRunSummary.d.ts","sourceRoot":"","sources":["../../../src/data/test-runner/TestRunSummary.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAG7C,MAAM,MAAM,mBAAmB,GAAG;IAChC,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,2CAA2C;IAC3C,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACtC,4DAA4D;IAC5D,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACpC,IAAI,EAAE,OAAO,CAAC;IACd,6DAA6D;IAC7D,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9B;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC,CAAC;AAiBF;;;GAGG;AACH,wBAAgB,cAAc,CAAC,EAC7B,KAAK,EACL,SAAS,EACT,OAAO,EACP,IAAI,EACJ,GAAG,EACH,OAAO,EACP,OAAe,EACf,SAAS,GACV,EAAE,mBAAmB,2CAyGrB"}
1
+ {"version":3,"file":"TestRunSummary.d.ts","sourceRoot":"","sources":["../../../src/data/test-runner/TestRunSummary.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAG7C,MAAM,MAAM,mBAAmB,GAAG;IAChC,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,2CAA2C;IAC3C,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACtC,4DAA4D;IAC5D,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACpC,IAAI,EAAE,OAAO,CAAC;IACd,6DAA6D;IAC7D,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9B;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC,CAAC;AAMF;;;GAGG;AACH,wBAAgB,cAAc,CAAC,EAC7B,KAAK,EACL,SAAS,EACT,OAAO,EACP,IAAI,EACJ,GAAG,EACH,OAAO,EACP,OAAe,EACf,SAAS,GACV,EAAE,mBAAmB,2CAyGrB"}
@@ -2,20 +2,10 @@ import { jsxs, jsx } from "react/jsx-runtime";
2
2
  import { Icon } from "../Icon.js";
3
3
  import { ProgressBar } from "../ProgressBar.js";
4
4
  import { cn } from "../../lib/utils.js";
5
- import { sumNonTaskTests } from "./status.js";
5
+ import { aggregateStatusCounts } from "./status.js";
6
6
  import { UiClock } from "../../icons/components/UiClock.js";
7
7
  import { UiCheck } from "../../icons/components/UiCheck.js";
8
8
  import { UiLoader } from "../../icons/components/UiLoader.js";
9
- function totals(tests) {
10
- return tests.reduce(
11
- (acc, t) => {
12
- const s = sumNonTaskTests(t);
13
- Object.keys(acc).forEach((k) => acc[k] += s[k]);
14
- return acc;
15
- },
16
- { total: 0, passed: 0, failed: 0, warned: 0, skipped: 0, pending: 0, running: 0, timedout: 0 }
17
- );
18
- }
19
9
  function pct(part, total) {
20
10
  return total > 0 ? Math.round(part / total * 100) : 0;
21
11
  }
@@ -29,7 +19,7 @@ function TestRunSummary({
29
19
  compact = false,
30
20
  className
31
21
  }) {
32
- const t = totals(tests);
22
+ const t = aggregateStatusCounts(tests);
33
23
  const clock = now ?? 0;
34
24
  const end = done && endTime ? endTime : clock;
35
25
  const elapsed = startTime && end >= startTime ? `${((end - startTime) / 1e3).toFixed(1)}s` : "";
@@ -1 +1 @@
1
- {"version":3,"file":"TestRunSummary.js","sources":["../../../src/data/test-runner/TestRunSummary.tsx"],"sourcesContent":["import { Icon } from \"../Icon\";\nimport { ProgressBar, type ProgressSegment } from \"../ProgressBar\";\nimport { cn } from \"../../lib/utils\";\nimport { UiCheck, UiClock, UiLoader } from \"../../icons\";\nimport type { RunMeta, Test } from \"./types\";\nimport { sumNonTaskTests, type StatusCounts } from \"./status\";\n\nexport type TestRunSummaryProps = {\n tests: Test[];\n /** Run start, epoch ms (host-provided). */\n startTime?: number | null | undefined;\n /** Run end, epoch ms; falls back to `now` while running. */\n endTime?: number | null | undefined;\n done: boolean;\n /** Injected clock (epoch ms) so the component stays pure. */\n now?: number | undefined;\n runMeta?: RunMeta | undefined;\n /**\n * Single-line layout for embedding in a host header (e.g. a dialog header):\n * counts + a short inline progress bar on one row, instead of the default\n * two-line right-aligned column.\n */\n compact?: boolean | undefined;\n className?: string | undefined;\n};\n\nfunction totals(tests: Test[]): StatusCounts {\n return tests.reduce(\n (acc, t) => {\n const s = sumNonTaskTests(t);\n (Object.keys(acc) as (keyof StatusCounts)[]).forEach((k) => (acc[k] += s[k]));\n return acc;\n },\n { total: 0, passed: 0, failed: 0, warned: 0, skipped: 0, pending: 0, running: 0, timedout: 0 },\n );\n}\n\nfunction pct(part: number, total: number): number {\n return total > 0 ? Math.round((part / total) * 100) : 0;\n}\n\n/**\n * Header summary: per-status counts, elapsed time, a stacked progress bar, and\n * pass/fail/pending gauge cards. Pure — elapsed time comes from injected `now`.\n */\nexport function TestRunSummary({\n tests,\n startTime,\n endTime,\n done,\n now,\n runMeta,\n compact = false,\n className,\n}: TestRunSummaryProps) {\n const t = totals(tests);\n const clock = now ?? 0;\n const end = done && endTime ? endTime : clock;\n const elapsed = startTime && end >= startTime ? `${((end - startTime) / 1000).toFixed(1)}s` : \"\";\n\n const segments: ProgressSegment[] = [\n { count: t.passed, color: \"bg-green-500\", label: \"passed\" },\n { count: t.skipped, color: \"bg-yellow-400\", label: \"skipped\" },\n { count: t.failed, color: \"bg-red-500\", label: \"failed\" },\n { count: t.timedout, color: \"bg-amber-500\", label: \"timed out\" },\n { count: t.running, color: \"bg-blue-400\", label: \"running\" },\n { count: t.pending, color: \"bg-muted-foreground/40\", label: \"queued\" },\n ];\n\n // Each status appears once, as a coloured count chip — no separate gauge-card\n // row repeating the same numbers as percentages.\n const chips: { n: number; label: string; tone: string }[] = [\n { n: t.passed, label: \"passed\", tone: \"text-green-600 dark:text-green-400\" },\n { n: t.failed, label: \"failed\", tone: \"text-red-600 dark:text-red-400\" },\n { n: t.timedout, label: \"timed out\", tone: \"text-amber-600 dark:text-amber-400\" },\n { n: t.skipped, label: \"skipped\", tone: \"text-yellow-700 dark:text-yellow-500\" },\n { n: t.running, label: \"running\", tone: \"text-blue-600 dark:text-blue-400\" },\n { n: t.pending, label: \"queued\", tone: \"text-muted-foreground\" },\n ];\n\n if (compact) {\n return (\n <div\n className={cn(\n \"flex flex-wrap items-center gap-x-3 gap-y-0.5 text-sm text-muted-foreground\",\n className,\n )}\n >\n <span className=\"font-medium text-foreground\">\n {t.total} tests\n {runMeta?.kind === \"rerun\" ? ` · rerun #${runMeta.sequence}` : \"\"}\n </span>\n {chips\n .filter((c) => c.n > 0)\n .map((c) => (\n <span key={c.label} className={c.tone}>\n {c.n} {c.label}\n </span>\n ))}\n {elapsed && (\n <span className=\"inline-flex items-center gap-1\">\n <Icon icon={UiClock} className=\"text-xs\" />\n {elapsed}\n </span>\n )}\n {t.total > 0 && (\n <span className=\"inline-flex items-center gap-1.5\">\n <ProgressBar className=\"w-24\" segments={segments} total={t.total} height=\"h-1.5\" />\n <span className=\"text-xs font-medium text-green-600 dark:text-green-400\">\n {pct(t.passed, t.total)}%\n </span>\n </span>\n )}\n {done ? (\n <Icon icon={UiCheck} className=\"text-green-600 dark:text-green-400\" />\n ) : (\n <Icon icon={UiLoader} className=\"animate-spin text-blue-500\" />\n )}\n </div>\n );\n }\n\n return (\n <div className={cn(\"flex min-w-[18rem] flex-col items-end gap-1.5\", className)}>\n <div className=\"flex flex-wrap items-center justify-end gap-x-3 gap-y-0.5 text-sm text-muted-foreground\">\n <span className=\"font-medium text-foreground\">\n {t.total} tests\n {runMeta?.kind === \"rerun\" ? ` · rerun #${runMeta.sequence}` : \"\"}\n </span>\n {chips\n .filter((c) => c.n > 0)\n .map((c) => (\n <span key={c.label} className={c.tone}>\n {c.n} {c.label}\n </span>\n ))}\n {elapsed && (\n <span className=\"inline-flex items-center gap-1\">\n <Icon icon={UiClock} className=\"text-xs\" />\n {elapsed}\n </span>\n )}\n {done ? (\n <Icon icon={UiCheck} className=\"text-green-600 dark:text-green-400\" />\n ) : (\n <Icon icon={UiLoader} className=\"animate-spin text-blue-500\" />\n )}\n </div>\n\n {t.total > 0 && (\n <div className=\"flex w-72 items-center gap-2\">\n <ProgressBar className=\"flex-1\" segments={segments} total={t.total} height=\"h-2\" />\n <span className=\"shrink-0 text-xs font-medium text-green-600 dark:text-green-400\">\n {pct(t.passed, t.total)}%\n </span>\n </div>\n )}\n </div>\n );\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"TestRunSummary.js","sources":["../../../src/data/test-runner/TestRunSummary.tsx"],"sourcesContent":["import { Icon } from \"../Icon\";\nimport { ProgressBar, type ProgressSegment } from \"../ProgressBar\";\nimport { cn } from \"../../lib/utils\";\nimport { UiCheck, UiClock, UiLoader } from \"../../icons\";\nimport type { RunMeta, Test } from \"./types\";\nimport { aggregateStatusCounts } from \"./status\";\n\nexport type TestRunSummaryProps = {\n tests: Test[];\n /** Run start, epoch ms (host-provided). */\n startTime?: number | null | undefined;\n /** Run end, epoch ms; falls back to `now` while running. */\n endTime?: number | null | undefined;\n done: boolean;\n /** Injected clock (epoch ms) so the component stays pure. */\n now?: number | undefined;\n runMeta?: RunMeta | undefined;\n /**\n * Single-line layout for embedding in a host header (e.g. a dialog header):\n * counts + a short inline progress bar on one row, instead of the default\n * two-line right-aligned column.\n */\n compact?: boolean | undefined;\n className?: string | undefined;\n};\n\nfunction pct(part: number, total: number): number {\n return total > 0 ? Math.round((part / total) * 100) : 0;\n}\n\n/**\n * Header summary: per-status counts, elapsed time, a stacked progress bar, and\n * pass/fail/pending gauge cards. Pure — elapsed time comes from injected `now`.\n */\nexport function TestRunSummary({\n tests,\n startTime,\n endTime,\n done,\n now,\n runMeta,\n compact = false,\n className,\n}: TestRunSummaryProps) {\n const t = aggregateStatusCounts(tests);\n const clock = now ?? 0;\n const end = done && endTime ? endTime : clock;\n const elapsed = startTime && end >= startTime ? `${((end - startTime) / 1000).toFixed(1)}s` : \"\";\n\n const segments: ProgressSegment[] = [\n { count: t.passed, color: \"bg-green-500\", label: \"passed\" },\n { count: t.skipped, color: \"bg-yellow-400\", label: \"skipped\" },\n { count: t.failed, color: \"bg-red-500\", label: \"failed\" },\n { count: t.timedout, color: \"bg-amber-500\", label: \"timed out\" },\n { count: t.running, color: \"bg-blue-400\", label: \"running\" },\n { count: t.pending, color: \"bg-muted-foreground/40\", label: \"queued\" },\n ];\n\n // Each status appears once, as a coloured count chip — no separate gauge-card\n // row repeating the same numbers as percentages.\n const chips: { n: number; label: string; tone: string }[] = [\n { n: t.passed, label: \"passed\", tone: \"text-green-600 dark:text-green-400\" },\n { n: t.failed, label: \"failed\", tone: \"text-red-600 dark:text-red-400\" },\n { n: t.timedout, label: \"timed out\", tone: \"text-amber-600 dark:text-amber-400\" },\n { n: t.skipped, label: \"skipped\", tone: \"text-yellow-700 dark:text-yellow-500\" },\n { n: t.running, label: \"running\", tone: \"text-blue-600 dark:text-blue-400\" },\n { n: t.pending, label: \"queued\", tone: \"text-muted-foreground\" },\n ];\n\n if (compact) {\n return (\n <div\n className={cn(\n \"flex flex-wrap items-center gap-x-3 gap-y-0.5 text-sm text-muted-foreground\",\n className,\n )}\n >\n <span className=\"font-medium text-foreground\">\n {t.total} tests\n {runMeta?.kind === \"rerun\" ? ` · rerun #${runMeta.sequence}` : \"\"}\n </span>\n {chips\n .filter((c) => c.n > 0)\n .map((c) => (\n <span key={c.label} className={c.tone}>\n {c.n} {c.label}\n </span>\n ))}\n {elapsed && (\n <span className=\"inline-flex items-center gap-1\">\n <Icon icon={UiClock} className=\"text-xs\" />\n {elapsed}\n </span>\n )}\n {t.total > 0 && (\n <span className=\"inline-flex items-center gap-1.5\">\n <ProgressBar className=\"w-24\" segments={segments} total={t.total} height=\"h-1.5\" />\n <span className=\"text-xs font-medium text-green-600 dark:text-green-400\">\n {pct(t.passed, t.total)}%\n </span>\n </span>\n )}\n {done ? (\n <Icon icon={UiCheck} className=\"text-green-600 dark:text-green-400\" />\n ) : (\n <Icon icon={UiLoader} className=\"animate-spin text-blue-500\" />\n )}\n </div>\n );\n }\n\n return (\n <div className={cn(\"flex min-w-[18rem] flex-col items-end gap-1.5\", className)}>\n <div className=\"flex flex-wrap items-center justify-end gap-x-3 gap-y-0.5 text-sm text-muted-foreground\">\n <span className=\"font-medium text-foreground\">\n {t.total} tests\n {runMeta?.kind === \"rerun\" ? ` · rerun #${runMeta.sequence}` : \"\"}\n </span>\n {chips\n .filter((c) => c.n > 0)\n .map((c) => (\n <span key={c.label} className={c.tone}>\n {c.n} {c.label}\n </span>\n ))}\n {elapsed && (\n <span className=\"inline-flex items-center gap-1\">\n <Icon icon={UiClock} className=\"text-xs\" />\n {elapsed}\n </span>\n )}\n {done ? (\n <Icon icon={UiCheck} className=\"text-green-600 dark:text-green-400\" />\n ) : (\n <Icon icon={UiLoader} className=\"animate-spin text-blue-500\" />\n )}\n </div>\n\n {t.total > 0 && (\n <div className=\"flex w-72 items-center gap-2\">\n <ProgressBar className=\"flex-1\" segments={segments} total={t.total} height=\"h-2\" />\n <span className=\"shrink-0 text-xs font-medium text-green-600 dark:text-green-400\">\n {pct(t.passed, t.total)}%\n </span>\n </div>\n )}\n </div>\n );\n}\n"],"names":[],"mappings":"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"}
@@ -13,16 +13,6 @@ const TestDetailPanel = require("./TestDetailPanel.cjs");
13
13
  const TestRunSummary = require("./TestRunSummary.cjs");
14
14
  const TestFilterBar = require("./TestFilterBar.cjs");
15
15
  const UiBeaker = require("../../icons/components/UiBeaker.cjs");
16
- function counts(tests) {
17
- return tests.reduce(
18
- (acc, t) => {
19
- const s = status.sumNonTaskTests(t);
20
- Object.keys(acc).forEach((k) => acc[k] += s[k]);
21
- return acc;
22
- },
23
- { total: 0, passed: 0, failed: 0, warned: 0, skipped: 0, pending: 0, running: 0, timedout: 0 }
24
- );
25
- }
26
16
  function toRegistry(adapters) {
27
17
  if (adapters instanceof adapter.TestNodeAdapterRegistry) return adapters;
28
18
  return adapter.createTestRunnerRegistry(adapters ?? []);
@@ -49,6 +39,7 @@ function TestRunner({
49
39
  title,
50
40
  showSummary = true,
51
41
  headerSlot,
42
+ hideHeader = false,
52
43
  defaultSplit = 45,
53
44
  startTime,
54
45
  endTime,
@@ -96,10 +87,10 @@ function TestRunner({
96
87
  ]
97
88
  );
98
89
  const frameworks = react.useMemo(() => status.collectFrameworks(tests), [tests]);
99
- const filterCounts = react.useMemo(() => counts(tests), [tests]);
90
+ const filterCounts = react.useMemo(() => status.aggregateStatusCounts(tests), [tests]);
100
91
  const hasContent = tests.length > 0;
101
92
  return /* @__PURE__ */ jsxRuntime.jsx(context.TestRunnerProvider, { value: ctx, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: utils.cn("flex h-full min-h-0 flex-col bg-background", className), children: [
102
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-b border-border bg-card px-density-4 py-density-3", children: [
93
+ !hideHeader && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-b border-border bg-card px-density-4 py-density-3", children: [
103
94
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-3", children: [
104
95
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
105
96
  title === void 0 ? /* @__PURE__ */ jsxRuntime.jsxs("h1", { className: "inline-flex items-center gap-1.5 text-lg font-bold", children: [