@opensip-cli/cli-ui 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +8 -0
  3. package/README.md +31 -0
  4. package/dist/banner.d.ts +70 -0
  5. package/dist/banner.d.ts.map +1 -0
  6. package/dist/banner.js +203 -0
  7. package/dist/banner.js.map +1 -0
  8. package/dist/clock.d.ts +28 -0
  9. package/dist/clock.d.ts.map +1 -0
  10. package/dist/clock.js +45 -0
  11. package/dist/clock.js.map +1 -0
  12. package/dist/error-message.d.ts +12 -0
  13. package/dist/error-message.d.ts.map +1 -0
  14. package/dist/error-message.js +13 -0
  15. package/dist/error-message.js.map +1 -0
  16. package/dist/fit-table-format.d.ts +35 -0
  17. package/dist/fit-table-format.d.ts.map +1 -0
  18. package/dist/fit-table-format.js +48 -0
  19. package/dist/fit-table-format.js.map +1 -0
  20. package/dist/format-duration.d.ts +13 -0
  21. package/dist/format-duration.d.ts.map +1 -0
  22. package/dist/format-duration.js +23 -0
  23. package/dist/format-duration.js.map +1 -0
  24. package/dist/index.d.ts +33 -0
  25. package/dist/index.d.ts.map +1 -0
  26. package/dist/index.js +32 -0
  27. package/dist/index.js.map +1 -0
  28. package/dist/live-progress.d.ts +57 -0
  29. package/dist/live-progress.d.ts.map +1 -0
  30. package/dist/live-progress.js +106 -0
  31. package/dist/live-progress.js.map +1 -0
  32. package/dist/progress-event.d.ts +66 -0
  33. package/dist/progress-event.d.ts.map +1 -0
  34. package/dist/progress-event.js +21 -0
  35. package/dist/progress-event.js.map +1 -0
  36. package/dist/project-header.d.ts +31 -0
  37. package/dist/project-header.d.ts.map +1 -0
  38. package/dist/project-header.js +38 -0
  39. package/dist/project-header.js.map +1 -0
  40. package/dist/render-to-ink.d.ts +15 -0
  41. package/dist/render-to-ink.d.ts.map +1 -0
  42. package/dist/render-to-ink.js +104 -0
  43. package/dist/render-to-ink.js.map +1 -0
  44. package/dist/render-to-text.d.ts +24 -0
  45. package/dist/render-to-text.d.ts.map +1 -0
  46. package/dist/render-to-text.js +86 -0
  47. package/dist/render-to-text.js.map +1 -0
  48. package/dist/run-footer-hints.d.ts +32 -0
  49. package/dist/run-footer-hints.d.ts.map +1 -0
  50. package/dist/run-footer-hints.js +33 -0
  51. package/dist/run-footer-hints.js.map +1 -0
  52. package/dist/run-header.d.ts +25 -0
  53. package/dist/run-header.d.ts.map +1 -0
  54. package/dist/run-header.js +19 -0
  55. package/dist/run-header.js.map +1 -0
  56. package/dist/run-summary.d.ts +48 -0
  57. package/dist/run-summary.d.ts.map +1 -0
  58. package/dist/run-summary.js +56 -0
  59. package/dist/run-summary.js.map +1 -0
  60. package/dist/run-timing-provider.d.ts +59 -0
  61. package/dist/run-timing-provider.d.ts.map +1 -0
  62. package/dist/run-timing-provider.js +52 -0
  63. package/dist/run-timing-provider.js.map +1 -0
  64. package/dist/spinner.d.ts +29 -0
  65. package/dist/spinner.d.ts.map +1 -0
  66. package/dist/spinner.js +44 -0
  67. package/dist/spinner.js.map +1 -0
  68. package/dist/theme.d.ts +42 -0
  69. package/dist/theme.d.ts.map +1 -0
  70. package/dist/theme.js +96 -0
  71. package/dist/theme.js.map +1 -0
  72. package/dist/verbose-detail.d.ts +43 -0
  73. package/dist/verbose-detail.d.ts.map +1 -0
  74. package/dist/verbose-detail.js +104 -0
  75. package/dist/verbose-detail.js.map +1 -0
  76. package/dist/view-model.d.ts +132 -0
  77. package/dist/view-model.d.ts.map +1 -0
  78. package/dist/view-model.js +71 -0
  79. package/dist/view-model.js.map +1 -0
  80. package/package.json +52 -0
@@ -0,0 +1,104 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * renderToInk — the interactive (TTY) interpreter.
4
+ *
5
+ * Walks the same `ViewNode` tree as `renderToText` and returns an Ink
6
+ * element, mapping each semantic `Tone` onto a `DEFAULT_THEME` token via
7
+ * `useTheme()` so the theme stays the single source of color truth. The
8
+ * visible text it produces is identical to `renderToText`'s output (the
9
+ * cross-renderer equivalence tests assert `stripAnsi(ink) === text`); the
10
+ * only thing Ink adds is color, weight, and dimming.
11
+ */
12
+ import { Box, Text } from 'ink';
13
+ import { useTheme } from './theme.js';
14
+ import { padTableCell, tableColumnWidths } from './view-model.js';
15
+ const SEPARATOR_WIDTH = 60;
16
+ const REGEX_META = /[.*+?^${}()|[\]\\]/g;
17
+ /** Map a semantic tone to a theme color token. `default` → no override. */
18
+ function toneColor(theme, tone) {
19
+ switch (tone) {
20
+ case 'brand': {
21
+ return theme.brand;
22
+ }
23
+ case 'success': {
24
+ return theme.success;
25
+ }
26
+ case 'error': {
27
+ return theme.error;
28
+ }
29
+ case 'warning': {
30
+ return theme.warning;
31
+ }
32
+ case 'info': {
33
+ return theme.info;
34
+ }
35
+ case 'muted': {
36
+ return theme.muted;
37
+ }
38
+ case 'default':
39
+ case undefined: {
40
+ return undefined;
41
+ }
42
+ }
43
+ }
44
+ function SpanText({ span }) {
45
+ const theme = useTheme();
46
+ return (_jsx(Text, { color: toneColor(theme, span.tone), bold: span.bold, dimColor: span.dim, children: span.text }));
47
+ }
48
+ /** Render one hint, bolding any configured substrings (longest-first). */
49
+ function HintText({ item }) {
50
+ const bolds = item.bold ?? [];
51
+ if (bolds.length === 0)
52
+ return _jsx(Text, { children: item.text });
53
+ const escaped = [...bolds]
54
+ .sort((a, b) => b.length - a.length)
55
+ .map((s) => s.replaceAll(REGEX_META, String.raw `\$&`));
56
+ const parts = item.text.split(new RegExp(`(${escaped.join('|')})`));
57
+ const boldSet = new Set(bolds);
58
+ return (_jsx(Text, { children: parts.map((p, i) => boldSet.has(p) ? (_jsx(Text, { bold: true, children: p }, i)) : (_jsx(Text, { children: p }, i))) }));
59
+ }
60
+ function NodeView({ node }) {
61
+ switch (node.kind) {
62
+ case 'line': {
63
+ return (_jsx(Text, { dimColor: node.dim, children: node.spans.map((s, i) => (_jsx(SpanText, { span: s }, i))) }));
64
+ }
65
+ case 'heading': {
66
+ return _jsx(HeadingView, { text: node.text, tone: node.tone });
67
+ }
68
+ case 'keyValues': {
69
+ return (_jsx(Box, { flexDirection: "column", children: node.pairs.map((p, i) => (_jsxs(Text, { children: [p.label, ": ", p.value] }, i))) }));
70
+ }
71
+ case 'table': {
72
+ const widths = tableColumnWidths(node.columns, node.rows);
73
+ const alignOf = (i) => node.align?.[i] ?? 'left';
74
+ const showHeader = node.showHeader !== false;
75
+ return (_jsxs(Box, { flexDirection: "column", children: [showHeader && (_jsx(Text, { dimColor: true, children: widths
76
+ .map((w, i) => (i > 0 ? ' ' : '') + padTableCell(node.columns[i] ?? '', w, alignOf(i)))
77
+ .join('') })), node.rows.map((cells, r) => (_jsx(Text, { children: widths.map((w, ci) => (_jsxs(Text, { children: [ci > 0 ? ' ' : '', _jsx(SpanText, { span: {
78
+ ...(cells[ci] ?? { text: '' }),
79
+ text: padTableCell(cells[ci]?.text ?? '', w, alignOf(ci)),
80
+ } })] }, ci))) }, r)))] }));
81
+ }
82
+ case 'hints': {
83
+ return (_jsx(Box, { paddingLeft: 2, children: _jsx(Text, { dimColor: true, children: node.items.map((h, i) => (_jsxs(Text, { children: [i > 0 ? ' | ' : '', _jsx(HintText, { item: h })] }, i))) }) }));
84
+ }
85
+ case 'separator': {
86
+ return _jsx(Text, { dimColor: true, children: '─'.repeat(SEPARATOR_WIDTH) });
87
+ }
88
+ case 'spacer': {
89
+ return _jsx(Text, { children: " " });
90
+ }
91
+ case 'group': {
92
+ return (_jsx(Box, { flexDirection: "column", paddingLeft: node.indent ?? 0, children: node.children.map((c, i) => (_jsx(NodeView, { node: c }, i))) }));
93
+ }
94
+ }
95
+ }
96
+ function HeadingView({ text, tone, }) {
97
+ const theme = useTheme();
98
+ return (_jsxs(Text, { bold: true, color: toneColor(theme, tone), children: ["== ", text, " =="] }));
99
+ }
100
+ /** Render a view-model node to an Ink element. */
101
+ export function renderToInk(node) {
102
+ return _jsx(NodeView, { node: node });
103
+ }
104
+ //# sourceMappingURL=render-to-ink.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render-to-ink.js","sourceRoot":"","sources":["../src/render-to-ink.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * renderToText — the non-interactive interpreter.
3
+ *
4
+ * Walks a `ViewNode` and returns a plain string for pipes, CI logs, and
5
+ * `> file` redirection. It drops `Tone`, `bold`, and `dim` entirely: the
6
+ * output carries no styling channel at all, which is stronger than
7
+ * `NO_COLOR` (that only zeroes theme colors). The guarantee this module
8
+ * upholds — and that its tests assert for every node kind — is that the
9
+ * output never contains an ANSI escape sequence.
10
+ *
11
+ * Pure string work; no `ink`/`react` import, so the piped path stays
12
+ * React-free (mirrors the cold-start discipline in the CLI render seam).
13
+ *
14
+ * Returns no trailing newline — the caller (the render seam) owns the
15
+ * final line break, matching how the Ink path appends one.
16
+ */
17
+ import type { ViewNode } from './view-model.js';
18
+ /**
19
+ * Render a view-model node to a plain string (no ANSI, no styling). The
20
+ * non-interactive counterpart to {@link renderToInk}; both consume the
21
+ * same `ViewNode`, so the piped and TTY forms cannot drift.
22
+ */
23
+ export declare function renderToText(node: ViewNode): string;
24
+ //# sourceMappingURL=render-to-text.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render-to-text.d.ts","sourceRoot":"","sources":["../src/render-to-text.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH,OAAO,KAAK,EAAkB,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AA8ChE;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CA2BnD"}
@@ -0,0 +1,86 @@
1
+ /**
2
+ * renderToText — the non-interactive interpreter.
3
+ *
4
+ * Walks a `ViewNode` and returns a plain string for pipes, CI logs, and
5
+ * `> file` redirection. It drops `Tone`, `bold`, and `dim` entirely: the
6
+ * output carries no styling channel at all, which is stronger than
7
+ * `NO_COLOR` (that only zeroes theme colors). The guarantee this module
8
+ * upholds — and that its tests assert for every node kind — is that the
9
+ * output never contains an ANSI escape sequence.
10
+ *
11
+ * Pure string work; no `ink`/`react` import, so the piped path stays
12
+ * React-free (mirrors the cold-start discipline in the CLI render seam).
13
+ *
14
+ * Returns no trailing newline — the caller (the render seam) owns the
15
+ * final line break, matching how the Ink path appends one.
16
+ */
17
+ import { padTableCell, tableColumnWidths } from './view-model.js';
18
+ const SEPARATOR_WIDTH = 60;
19
+ function spansToText(spans) {
20
+ return spans.map((s) => s.text).join('');
21
+ }
22
+ function hintsToText(items) {
23
+ // Two-space indent + " | "-joined, matching the RunFooterHints strip.
24
+ // Bold substrings are a styling concern and carry no plain-text marker.
25
+ return ` ${items.map((h) => h.text).join(' | ')}`;
26
+ }
27
+ /**
28
+ * Column-aligned table rendering: every cell is padded to its column width
29
+ * (shared `tableColumnWidths`, so the pipe form lines up exactly as the TTY
30
+ * form). Trailing whitespace is trimmed to keep the ANSI-free output clean.
31
+ */
32
+ function tableToText(columns, rows, align, showHeader) {
33
+ const widths = tableColumnWidths(columns, rows);
34
+ const alignOf = (i) => align?.[i] ?? 'left';
35
+ const renderRow = (cells) => widths
36
+ .map((w, i) => padTableCell(cells[i]?.text ?? '', w, alignOf(i)))
37
+ .join(' ')
38
+ .trimEnd();
39
+ const lines = showHeader ? [renderRow(columns.map((c) => ({ text: c })))] : [];
40
+ for (const cells of rows)
41
+ lines.push(renderRow(cells));
42
+ return lines.join('\n');
43
+ }
44
+ function indentLines(block, by) {
45
+ if (by <= 0)
46
+ return block;
47
+ const pad = ' '.repeat(by);
48
+ return block
49
+ .split('\n')
50
+ .map((l) => (l.length > 0 ? pad + l : l))
51
+ .join('\n');
52
+ }
53
+ /**
54
+ * Render a view-model node to a plain string (no ANSI, no styling). The
55
+ * non-interactive counterpart to {@link renderToInk}; both consume the
56
+ * same `ViewNode`, so the piped and TTY forms cannot drift.
57
+ */
58
+ export function renderToText(node) {
59
+ switch (node.kind) {
60
+ case 'line': {
61
+ return spansToText(node.spans);
62
+ }
63
+ case 'heading': {
64
+ return `== ${node.text} ==`;
65
+ }
66
+ case 'keyValues': {
67
+ return node.pairs.map((p) => `${p.label}: ${p.value}`).join('\n');
68
+ }
69
+ case 'table': {
70
+ return tableToText(node.columns, node.rows, node.align, node.showHeader !== false);
71
+ }
72
+ case 'hints': {
73
+ return hintsToText(node.items);
74
+ }
75
+ case 'separator': {
76
+ return '─'.repeat(SEPARATOR_WIDTH);
77
+ }
78
+ case 'spacer': {
79
+ return '';
80
+ }
81
+ case 'group': {
82
+ return indentLines(node.children.map(renderToText).join('\n'), node.indent ?? 0);
83
+ }
84
+ }
85
+ }
86
+ //# sourceMappingURL=render-to-text.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render-to-text.js","sourceRoot":"","sources":["../src/render-to-text.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAIlE,MAAM,eAAe,GAAG,EAAE,CAAC;AAE3B,SAAS,WAAW,CAAC,KAAsB;IACzC,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAC3C,CAAC;AAED,SAAS,WAAW,CAAC,KAA0B;IAC7C,sEAAsE;IACtE,wEAAwE;IACxE,OAAO,KAAK,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;AACrD,CAAC;AAED;;;;GAIG;AACH,SAAS,WAAW,CAClB,OAA0B,EAC1B,IAAkC,EAClC,KAAgD,EAChD,UAAmB;IAEnB,MAAM,MAAM,GAAG,iBAAiB,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAChD,MAAM,OAAO,GAAG,CAAC,CAAS,EAAoB,EAAE,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC;IACtE,MAAM,SAAS,GAAG,CAAC,KAAkC,EAAU,EAAE,CAC/D,MAAM;SACH,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,IAAI,EAAE,EAAE,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;SAChE,IAAI,CAAC,IAAI,CAAC;SACV,OAAO,EAAE,CAAC;IACf,MAAM,KAAK,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC/E,KAAK,MAAM,KAAK,IAAI,IAAI;QAAE,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;IACvD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,SAAS,WAAW,CAAC,KAAa,EAAE,EAAU;IAC5C,IAAI,EAAE,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IAC1B,MAAM,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAC3B,OAAO,KAAK;SACT,KAAK,CAAC,IAAI,CAAC;SACX,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;SACxC,IAAI,CAAC,IAAI,CAAC,CAAC;AAChB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,IAAc;IACzC,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;QAClB,KAAK,MAAM,CAAC,CAAC,CAAC;YACZ,OAAO,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACjC,CAAC;QACD,KAAK,SAAS,CAAC,CAAC,CAAC;YACf,OAAO,MAAM,IAAI,CAAC,IAAI,KAAK,CAAC;QAC9B,CAAC;QACD,KAAK,WAAW,CAAC,CAAC,CAAC;YACjB,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACpE,CAAC;QACD,KAAK,OAAO,CAAC,CAAC,CAAC;YACb,OAAO,WAAW,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,UAAU,KAAK,KAAK,CAAC,CAAC;QACrF,CAAC;QACD,KAAK,OAAO,CAAC,CAAC,CAAC;YACb,OAAO,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACjC,CAAC;QACD,KAAK,WAAW,CAAC,CAAC,CAAC;YACjB,OAAO,GAAG,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;QACrC,CAAC;QACD,KAAK,QAAQ,CAAC,CAAC,CAAC;YACd,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,KAAK,OAAO,CAAC,CAAC,CAAC;YACb,OAAO,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC;QACnF,CAAC;IACH,CAAC;AACH,CAAC"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * RunFooterHints — shared bottom-of-output hint strip used by every Ink
3
+ * live view's done state.
4
+ *
5
+ * The format is fixed: a dim, two-space-indented line listing the
6
+ * next-step flags a user is likely to reach for, separated by " | ". Each
7
+ * tool supplies its own hints so the strip can name tool-specific flags
8
+ * (e.g. `--verbose`, `--report-to <url>`) while the surrounding visual
9
+ * treatment stays consistent.
10
+ *
11
+ * The strip lives once, as the `viewFooterHints` view-model producer. The
12
+ * Ink component renders it via `renderToInk`; the non-interactive path
13
+ * renders the same view through `renderToText`, so the two cannot drift.
14
+ * (The bold-substring tokenization is owned by the Ink interpreter's hint
15
+ * renderer.)
16
+ */
17
+ import React from 'react';
18
+ import type { ViewNode } from './view-model.js';
19
+ export interface RunFooterHint {
20
+ /** Plain-text line to render. Substrings matching `bold` are bolded. */
21
+ readonly text: string;
22
+ /** Substrings within `text` to render bold. May be empty. */
23
+ readonly bold?: readonly string[];
24
+ }
25
+ export interface RunFooterHintsProps {
26
+ readonly hints: readonly RunFooterHint[];
27
+ }
28
+ /** The hint strip as a renderer-agnostic view-model node. */
29
+ export declare function viewFooterHints(hints: readonly RunFooterHint[]): ViewNode;
30
+ /** Ink view of {@link viewFooterHints}; renders nothing when there are no hints. */
31
+ export declare function RunFooterHints({ hints }: RunFooterHintsProps): React.ReactElement | null;
32
+ //# sourceMappingURL=run-footer-hints.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-footer-hints.d.ts","sourceRoot":"","sources":["../src/run-footer-hints.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAEhD,MAAM,WAAW,aAAa;IAC5B,wEAAwE;IACxE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,6DAA6D;IAC7D,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACnC;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,KAAK,EAAE,SAAS,aAAa,EAAE,CAAC;CAC1C;AAED,6DAA6D;AAC7D,wBAAgB,eAAe,CAAC,KAAK,EAAE,SAAS,aAAa,EAAE,GAAG,QAAQ,CAKzE;AAED,oFAAoF;AACpF,wBAAgB,cAAc,CAAC,EAAE,KAAK,EAAE,EAAE,mBAAmB,GAAG,KAAK,CAAC,YAAY,GAAG,IAAI,CAGxF"}
@@ -0,0 +1,33 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /**
3
+ * RunFooterHints — shared bottom-of-output hint strip used by every Ink
4
+ * live view's done state.
5
+ *
6
+ * The format is fixed: a dim, two-space-indented line listing the
7
+ * next-step flags a user is likely to reach for, separated by " | ". Each
8
+ * tool supplies its own hints so the strip can name tool-specific flags
9
+ * (e.g. `--verbose`, `--report-to <url>`) while the surrounding visual
10
+ * treatment stays consistent.
11
+ *
12
+ * The strip lives once, as the `viewFooterHints` view-model producer. The
13
+ * Ink component renders it via `renderToInk`; the non-interactive path
14
+ * renders the same view through `renderToText`, so the two cannot drift.
15
+ * (The bold-substring tokenization is owned by the Ink interpreter's hint
16
+ * renderer.)
17
+ */
18
+ import { Box } from 'ink';
19
+ import { renderToInk } from './render-to-ink.js';
20
+ /** The hint strip as a renderer-agnostic view-model node. */
21
+ export function viewFooterHints(hints) {
22
+ return {
23
+ kind: 'hints',
24
+ items: hints.map((h) => (h.bold ? { text: h.text, bold: h.bold } : { text: h.text })),
25
+ };
26
+ }
27
+ /** Ink view of {@link viewFooterHints}; renders nothing when there are no hints. */
28
+ export function RunFooterHints({ hints }) {
29
+ if (hints.length === 0)
30
+ return null;
31
+ return _jsx(Box, { paddingTop: 1, children: renderToInk(viewFooterHints(hints)) });
32
+ }
33
+ //# sourceMappingURL=run-footer-hints.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-footer-hints.js","sourceRoot":"","sources":["../src/run-footer-hints.tsx"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAG1B,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAejD,6DAA6D;AAC7D,MAAM,UAAU,eAAe,CAAC,KAA+B;IAC7D,OAAO;QACL,IAAI,EAAE,OAAO;QACb,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;KACtF,CAAC;AACJ,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,cAAc,CAAC,EAAE,KAAK,EAAuB;IAC3D,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACpC,OAAO,KAAC,GAAG,IAAC,UAAU,EAAE,CAAC,YAAG,WAAW,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC,GAAO,CAAC;AACzE,CAAC"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * RunHeader — tool header shown after the banner + project line for each
3
+ * tool run. Displays the tool name, optional metadata key-value pairs,
4
+ * and an optional description, followed by a separator line.
5
+ *
6
+ * The project-location line is NOT rendered here — {@link ProjectHeader}
7
+ * is the single canonical renderer of `ℹ Project: <root>`, mounted by the
8
+ * App shell and each live view directly under the banner. RunHeader is
9
+ * purely the per-tool title/metadata band.
10
+ */
11
+ import React from 'react';
12
+ export interface RunHeaderMeta {
13
+ readonly label: string;
14
+ readonly value: string;
15
+ }
16
+ export interface RunHeaderProps {
17
+ /** Title rendered in brand color, e.g. `Fitness Checks`, `Code Graph`. */
18
+ readonly tool: string;
19
+ /** Optional description shown below the metadata row. */
20
+ readonly description?: string;
21
+ /** Metadata pairs rendered in the dim band below the title. */
22
+ readonly metadata?: readonly RunHeaderMeta[];
23
+ }
24
+ export declare function RunHeader({ tool, description, metadata, }: RunHeaderProps): React.ReactElement;
25
+ //# sourceMappingURL=run-header.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-header.d.ts","sourceRoot":"","sources":["../src/run-header.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,cAAc;IAC7B,0EAA0E;IAC1E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,yDAAyD;IACzD,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,+DAA+D;IAC/D,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,aAAa,EAAE,CAAC;CAC9C;AAED,wBAAgB,SAAS,CAAC,EACxB,IAAI,EACJ,WAAW,EACX,QAAa,GACd,EAAE,cAAc,GAAG,KAAK,CAAC,YAAY,CAsBrC"}
@@ -0,0 +1,19 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * RunHeader — tool header shown after the banner + project line for each
4
+ * tool run. Displays the tool name, optional metadata key-value pairs,
5
+ * and an optional description, followed by a separator line.
6
+ *
7
+ * The project-location line is NOT rendered here — {@link ProjectHeader}
8
+ * is the single canonical renderer of `ℹ Project: <root>`, mounted by the
9
+ * App shell and each live view directly under the banner. RunHeader is
10
+ * purely the per-tool title/metadata band.
11
+ */
12
+ import { Text, Box } from 'ink';
13
+ import { useTheme } from './theme.js';
14
+ export function RunHeader({ tool, description, metadata = [], }) {
15
+ const theme = useTheme();
16
+ const separator = '─'.repeat(60);
17
+ return (_jsxs(Box, { flexDirection: "column", paddingLeft: 2, paddingTop: 1, children: [_jsx(Text, { bold: true, color: theme.brand, children: tool }), metadata.length > 0 && (_jsx(Text, { dimColor: true, children: metadata.map((m) => `${m.label}: ${m.value}`).join(' ') })), description !== undefined && (_jsxs(_Fragment, { children: [_jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: description })] })), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: separator })] }));
18
+ }
19
+ //# sourceMappingURL=run-header.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-header.js","sourceRoot":"","sources":["../src/run-header.tsx"],"names":[],"mappings":";AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAGhC,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAgBtC,MAAM,UAAU,SAAS,CAAC,EACxB,IAAI,EACJ,WAAW,EACX,QAAQ,GAAG,EAAE,GACE;IACf,MAAM,KAAK,GAAG,QAAQ,EAAE,CAAC;IACzB,MAAM,SAAS,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAEjC,OAAO,CACL,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,EAAC,WAAW,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,aACvD,KAAC,IAAI,IAAC,IAAI,QAAC,KAAK,EAAE,KAAK,CAAC,KAAK,YAC1B,IAAI,GACA,EACN,QAAQ,CAAC,MAAM,GAAG,CAAC,IAAI,CACtB,KAAC,IAAI,IAAC,QAAQ,kBAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAQ,CAClF,EACA,WAAW,KAAK,SAAS,IAAI,CAC5B,8BACE,KAAC,IAAI,oBAAS,EACd,KAAC,IAAI,IAAC,QAAQ,kBAAE,WAAW,GAAQ,IAClC,CACJ,EACD,KAAC,IAAI,oBAAS,EACd,KAAC,IAAI,IAAC,QAAQ,kBAAE,SAAS,GAAQ,IAC7B,CACP,CAAC;AACJ,CAAC"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * RunSummary — shared one-line PASS/FAIL summary used by every Ink live
3
+ * view in the suite (fitness, graph, sim).
4
+ *
5
+ * The format is fixed: `{PASS|FAIL} ({E} Errors, {W} Warnings) | Duration {dur}`
6
+ * (ADR-0035). The leading token is the run's single verdict
7
+ * (`envelope.verdict.passed`) — the same value that drives the exit code — so
8
+ * the headline answers "did this run pass?" directly; the error/warning counts
9
+ * are the detail. A clean run reads `PASS (0 Errors, 0 Warnings)` (no more
10
+ * misleading `0 Passed, 0 Failed`). Per-unit pass/fail lives in the table below.
11
+ * Colors are theme-driven (success/error for the verdict; error/warning for
12
+ * nonzero counts, muted when zero).
13
+ *
14
+ * The format lives once, as the `viewRunSummary` view-model producer. The
15
+ * Ink component is a thin wrapper that renders that view; the
16
+ * non-interactive (piped/CI) path renders the same view through
17
+ * `renderToText`, so the two cannot drift. (Previously the plain-text form
18
+ * was hand-retyped in graph's `writeRunSummaryPlain`.)
19
+ */
20
+ import React from 'react';
21
+ import { type ViewNode } from './view-model.js';
22
+ export interface RunSummaryProps {
23
+ /** The run's single verdict (ADR-0035) — the headline PASS/FAIL token. */
24
+ readonly passed: boolean;
25
+ readonly errors: number;
26
+ readonly warnings: number;
27
+ /**
28
+ * Explicit duration. When omitted, the component reads from the nearest
29
+ * `RunTimingProvider` (host-owned run timer). This is the preferred path
30
+ * after the host-owned-run-timing migration.
31
+ *
32
+ * Kept optional for compat during Phases 3-5 (tools still passing the old
33
+ * per-tool numbers); once tools omit it the provider supplies the value
34
+ * that matches the persisted StoredSession.
35
+ */
36
+ readonly durationMs?: number;
37
+ }
38
+ /**
39
+ * The canonical summary line as a renderer-agnostic view-model node. Span
40
+ * text concatenates to exactly:
41
+ * `{PASS|FAIL} ({E} Errors, {W} Warnings) | Duration {dur}`
42
+ */
43
+ export declare function viewRunSummary({ passed, errors, warnings, durationMs, }: RunSummaryProps): ViewNode;
44
+ /** Ink view of {@link viewRunSummary}. Indented to `paddingLeft={2}` so the
45
+ * live summary line aligns with the run header + footer hints (both also at 2)
46
+ * instead of sitting flush-left against the indented rest of the output. */
47
+ export declare function RunSummary(props: RunSummaryProps): React.ReactElement;
48
+ //# sourceMappingURL=run-summary.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-summary.d.ts","sourceRoot":"","sources":["../src/run-summary.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,KAAK,MAAM,OAAO,CAAC;AAK1B,OAAO,EAAmB,KAAK,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAEjE,MAAM,WAAW,eAAe;IAC9B,0EAA0E;IAC1E,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B;;;;;;;;OAQG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,EAC7B,MAAM,EACN,MAAM,EACN,QAAQ,EACR,UAAc,GACf,EAAE,eAAe,GAAG,QAAQ,CAa5B;AAED;;6EAE6E;AAC7E,wBAAgB,UAAU,CAAC,KAAK,EAAE,eAAe,GAAG,KAAK,CAAC,YAAY,CAWrE"}
@@ -0,0 +1,56 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /**
3
+ * RunSummary — shared one-line PASS/FAIL summary used by every Ink live
4
+ * view in the suite (fitness, graph, sim).
5
+ *
6
+ * The format is fixed: `{PASS|FAIL} ({E} Errors, {W} Warnings) | Duration {dur}`
7
+ * (ADR-0035). The leading token is the run's single verdict
8
+ * (`envelope.verdict.passed`) — the same value that drives the exit code — so
9
+ * the headline answers "did this run pass?" directly; the error/warning counts
10
+ * are the detail. A clean run reads `PASS (0 Errors, 0 Warnings)` (no more
11
+ * misleading `0 Passed, 0 Failed`). Per-unit pass/fail lives in the table below.
12
+ * Colors are theme-driven (success/error for the verdict; error/warning for
13
+ * nonzero counts, muted when zero).
14
+ *
15
+ * The format lives once, as the `viewRunSummary` view-model producer. The
16
+ * Ink component is a thin wrapper that renders that view; the
17
+ * non-interactive (piped/CI) path renders the same view through
18
+ * `renderToText`, so the two cannot drift. (Previously the plain-text form
19
+ * was hand-retyped in graph's `writeRunSummaryPlain`.)
20
+ */
21
+ import { Box } from 'ink';
22
+ import { formatDuration } from './format-duration.js';
23
+ import { renderToInk } from './render-to-ink.js';
24
+ import { useRunDuration } from './run-timing-provider.js';
25
+ import { line } from './view-model.js';
26
+ /**
27
+ * The canonical summary line as a renderer-agnostic view-model node. Span
28
+ * text concatenates to exactly:
29
+ * `{PASS|FAIL} ({E} Errors, {W} Warnings) | Duration {dur}`
30
+ */
31
+ export function viewRunSummary({ passed, errors, warnings, durationMs = 0, }) {
32
+ const spans = [
33
+ { text: passed ? 'PASS' : 'FAIL', tone: passed ? 'success' : 'error' },
34
+ { text: ' (' },
35
+ { text: `${errors} Errors`, tone: errors > 0 ? 'error' : 'muted' },
36
+ { text: ', ' },
37
+ { text: `${warnings} Warnings`, tone: warnings > 0 ? 'warning' : 'muted' },
38
+ { text: ') ' },
39
+ { text: '|', dim: true },
40
+ { text: ' Duration ' },
41
+ { text: formatDuration(durationMs), tone: 'info' },
42
+ ];
43
+ return line(spans);
44
+ }
45
+ /** Ink view of {@link viewRunSummary}. Indented to `paddingLeft={2}` so the
46
+ * live summary line aligns with the run header + footer hints (both also at 2)
47
+ * instead of sitting flush-left against the indented rest of the output. */
48
+ export function RunSummary(props) {
49
+ // When durationMs omitted, read from the RunTimingProvider (host timer).
50
+ // This makes the displayed duration match the value that will be (or was)
51
+ // recorded to StoredSession via the host `runSession.record` seam.
52
+ const resolvedDuration = props.durationMs ?? useRunDuration();
53
+ const viewProps = { ...props, durationMs: resolvedDuration };
54
+ return (_jsx(Box, { paddingTop: 1, paddingLeft: 2, children: renderToInk(viewRunSummary(viewProps)) }));
55
+ }
56
+ //# sourceMappingURL=run-summary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-summary.js","sourceRoot":"","sources":["../src/run-summary.tsx"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAG1B,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,EAAE,IAAI,EAA4B,MAAM,iBAAiB,CAAC;AAmBjE;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,EAC7B,MAAM,EACN,MAAM,EACN,QAAQ,EACR,UAAU,GAAG,CAAC,GACE;IAChB,MAAM,KAAK,GAAW;QACpB,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,EAAE;QACtE,EAAE,IAAI,EAAE,KAAK,EAAE;QACf,EAAE,IAAI,EAAE,GAAG,MAAM,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,EAAE;QAClE,EAAE,IAAI,EAAE,IAAI,EAAE;QACd,EAAE,IAAI,EAAE,GAAG,QAAQ,WAAW,EAAE,IAAI,EAAE,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,EAAE;QAC1E,EAAE,IAAI,EAAE,IAAI,EAAE;QACd,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE;QACxB,EAAE,IAAI,EAAE,YAAY,EAAE;QACtB,EAAE,IAAI,EAAE,cAAc,CAAC,UAAU,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE;KACnD,CAAC;IACF,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC;AACrB,CAAC;AAED;;6EAE6E;AAC7E,MAAM,UAAU,UAAU,CAAC,KAAsB;IAC/C,yEAAyE;IACzE,0EAA0E;IAC1E,mEAAmE;IACnE,MAAM,gBAAgB,GAAG,KAAK,CAAC,UAAU,IAAI,cAAc,EAAE,CAAC;IAC9D,MAAM,SAAS,GAAoB,EAAE,GAAG,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,CAAC;IAC9E,OAAO,CACL,KAAC,GAAG,IAAC,UAAU,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,YAC/B,WAAW,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC,GACnC,CACP,CAAC;AACJ,CAAC"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * RunTimingProvider + hooks — React context bridge for the host-owned
3
+ * `RunTimer` (host-owned-run-timing plan).
4
+ *
5
+ * Live views (and any nested RunSummary) receive the *exact same* timer
6
+ * instance that the host stamped into `ToolCliContext.runSession.timing` (and
7
+ * forwarded via the optional `LiveViewContext` second arg to the renderer).
8
+ *
9
+ * This lets the UI read wall duration without tools threading numbers, and
10
+ * ensures the persisted StoredSession.durationMs (from the host snapshot at
11
+ * record time) and the final "Duration X" line agree.
12
+ *
13
+ * Usage in a live renderer:
14
+ * <RunTimingProvider timer={context.runSession.timing}>
15
+ * <RunSummary passed=... errors=... warnings=... />
16
+ * </RunTimingProvider>
17
+ *
18
+ * Components can omit durationMs and read from the provider.
19
+ */
20
+ import React, { type ReactNode } from 'react';
21
+ /**
22
+ * Structural shape of the host `RunTimer` (from `@opensip-cli/core`) that this
23
+ * presentational kit consumes. Defined locally so `@opensip-cli/cli-ui` keeps
24
+ * ZERO workspace dependencies — it is a pure Ink/React primitives package and
25
+ * must not depend on the kernel (enforced by dependency-cruiser). Core's
26
+ * `RunTimer` is structurally assignable, so callers pass `cli.runSession.timing`
27
+ * (or `LiveViewContext.runSession.timing`) unchanged.
28
+ */
29
+ export interface RunTimerLike {
30
+ /** Monotonic elapsed time since the run started, in milliseconds. */
31
+ elapsedMs(): number;
32
+ }
33
+ /** Props for the provider that wraps a live view subtree. */
34
+ export interface RunTimingProviderProps {
35
+ /** The host timer from `cli.runSession.timing` (or LiveViewContext). */
36
+ readonly timer: RunTimerLike;
37
+ readonly children: ReactNode;
38
+ }
39
+ /**
40
+ * Provides a `RunTimer` to descendants. Safe to nest; inner wins.
41
+ * The timer is a stable object for the whole run — its `elapsedMs()` and
42
+ * `snapshot()` are what produce live ticking + final recorded duration.
43
+ */
44
+ export declare function RunTimingProvider({ timer, children }: RunTimingProviderProps): React.JSX.Element;
45
+ /**
46
+ * Read the current host `RunTimer` (or null if no provider in tree).
47
+ * Components should handle null (falls back to explicit prop or 0).
48
+ */
49
+ export declare function useRunTiming(): RunTimerLike | null;
50
+ /**
51
+ * Convenience: current elapsed duration in ms.
52
+ * - During a live run: reflects `timer.elapsedMs()` at render time (ticking UX).
53
+ * - After done: a consumer can snapshot once and pass an explicit durationMs
54
+ * if they want a frozen completed value (see RunSummary).
55
+ *
56
+ * Returns 0 when no provider (safe default).
57
+ */
58
+ export declare function useRunDuration(): number;
59
+ //# sourceMappingURL=run-timing-provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-timing-provider.d.ts","sourceRoot":"","sources":["../src/run-timing-provider.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,EAA6B,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAEzE;;;;;;;GAOG;AACH,MAAM,WAAW,YAAY;IAC3B,qEAAqE;IACrE,SAAS,IAAI,MAAM,CAAC;CACrB;AAID,6DAA6D;AAC7D,MAAM,WAAW,sBAAsB;IACrC,wEAAwE;IACxE,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC;CAC9B;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,sBAAsB,qBAE5E;AAED;;;GAGG;AACH,wBAAgB,YAAY,IAAI,YAAY,GAAG,IAAI,CAElD;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,IAAI,MAAM,CAIvC"}
@@ -0,0 +1,52 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /**
3
+ * RunTimingProvider + hooks — React context bridge for the host-owned
4
+ * `RunTimer` (host-owned-run-timing plan).
5
+ *
6
+ * Live views (and any nested RunSummary) receive the *exact same* timer
7
+ * instance that the host stamped into `ToolCliContext.runSession.timing` (and
8
+ * forwarded via the optional `LiveViewContext` second arg to the renderer).
9
+ *
10
+ * This lets the UI read wall duration without tools threading numbers, and
11
+ * ensures the persisted StoredSession.durationMs (from the host snapshot at
12
+ * record time) and the final "Duration X" line agree.
13
+ *
14
+ * Usage in a live renderer:
15
+ * <RunTimingProvider timer={context.runSession.timing}>
16
+ * <RunSummary passed=... errors=... warnings=... />
17
+ * </RunTimingProvider>
18
+ *
19
+ * Components can omit durationMs and read from the provider.
20
+ */
21
+ import { createContext, useContext } from 'react';
22
+ const RunTimingContext = createContext(null);
23
+ /**
24
+ * Provides a `RunTimer` to descendants. Safe to nest; inner wins.
25
+ * The timer is a stable object for the whole run — its `elapsedMs()` and
26
+ * `snapshot()` are what produce live ticking + final recorded duration.
27
+ */
28
+ export function RunTimingProvider({ timer, children }) {
29
+ return _jsx(RunTimingContext.Provider, { value: timer, children: children });
30
+ }
31
+ /**
32
+ * Read the current host `RunTimer` (or null if no provider in tree).
33
+ * Components should handle null (falls back to explicit prop or 0).
34
+ */
35
+ export function useRunTiming() {
36
+ return useContext(RunTimingContext);
37
+ }
38
+ /**
39
+ * Convenience: current elapsed duration in ms.
40
+ * - During a live run: reflects `timer.elapsedMs()` at render time (ticking UX).
41
+ * - After done: a consumer can snapshot once and pass an explicit durationMs
42
+ * if they want a frozen completed value (see RunSummary).
43
+ *
44
+ * Returns 0 when no provider (safe default).
45
+ */
46
+ export function useRunDuration() {
47
+ const timer = useRunTiming();
48
+ if (!timer)
49
+ return 0;
50
+ return timer.elapsedMs();
51
+ }
52
+ //# sourceMappingURL=run-timing-provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-timing-provider.js","sourceRoot":"","sources":["../src/run-timing-provider.tsx"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAc,EAAE,aAAa,EAAE,UAAU,EAAkB,MAAM,OAAO,CAAC;AAezE,MAAM,gBAAgB,GAAG,aAAa,CAAsB,IAAI,CAAC,CAAC;AASlE;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,EAAE,KAAK,EAAE,QAAQ,EAA0B;IAC3E,OAAO,KAAC,gBAAgB,CAAC,QAAQ,IAAC,KAAK,EAAE,KAAK,YAAG,QAAQ,GAA6B,CAAC;AACzF,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY;IAC1B,OAAO,UAAU,CAAC,gBAAgB,CAAC,CAAC;AACtC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,cAAc;IAC5B,MAAM,KAAK,GAAG,YAAY,EAAE,CAAC;IAC7B,IAAI,CAAC,KAAK;QAAE,OAAO,CAAC,CAAC;IACrB,OAAO,KAAK,CAAC,SAAS,EAAE,CAAC;AAC3B,CAAC"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Spinner — animated progress indicator. Renders one braille frame per tick
3
+ * plus an optional `completed/total (pct%)` suffix.
4
+ *
5
+ * Two modes:
6
+ * - Inside a `<ClockProvider>`: consumes the provider's tick via
7
+ * `useClock()`. Multiple spinners share the same timer.
8
+ * - Outside a provider (default): owns its own interval via `useTick()`.
9
+ * Convenient for single-spinner screens where setting up a provider
10
+ * is unnecessary.
11
+ */
12
+ import React from 'react';
13
+ /** Spinner frame keyed off the surrounding `<ClockProvider>`. */
14
+ export declare function useSpinner(): string;
15
+ /** Spinner frame from a self-owned interval — provider-free. */
16
+ export declare function useStandaloneSpinner(intervalMs?: number): string;
17
+ export interface SpinnerProps {
18
+ readonly total: number;
19
+ readonly completed: number;
20
+ readonly label?: string;
21
+ /**
22
+ * When true, the spinner uses its own internal tick instead of consuming
23
+ * a `<ClockProvider>`. Useful for tool runners that don't want to wrap
24
+ * their tree in a provider for a single spinner.
25
+ */
26
+ readonly standalone?: boolean;
27
+ }
28
+ export declare function Spinner({ total, completed, label, standalone, }: SpinnerProps): React.ReactElement;
29
+ //# sourceMappingURL=spinner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spinner.d.ts","sourceRoot":"","sources":["../src/spinner.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,KAAK,MAAM,OAAO,CAAC;AAO1B,iEAAiE;AACjE,wBAAgB,UAAU,IAAI,MAAM,CAGnC;AAED,gEAAgE;AAChE,wBAAgB,oBAAoB,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,CAGhE;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB;;;;OAIG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED,wBAAgB,OAAO,CAAC,EACtB,KAAK,EACL,SAAS,EACT,KAAoB,EACpB,UAAkB,GACnB,EAAE,YAAY,GAAG,KAAK,CAAC,YAAY,CAMnC"}