@vitest-agent/ui 1.0.5 → 1.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.
- package/index.d.ts +48 -4
- package/index.js +4 -3
- package/package.json +2 -2
- package/render-ink/CountColumns.js +15 -8
- package/render-ink/ProjectRow.js +9 -9
- package/render-ink/StreamApp.js +35 -20
- package/render-ink/TagColumns.js +49 -0
- package/render-ink/index.js +3 -2
- package/render-ink/tag-suffix.js +0 -18
package/index.d.ts
CHANGED
|
@@ -1476,9 +1476,19 @@ interface CountColumnsProps {
|
|
|
1476
1476
|
/** Number of timed-out tests. */
|
|
1477
1477
|
readonly timeoutCount: number;
|
|
1478
1478
|
}
|
|
1479
|
+
/**
|
|
1480
|
+
* Fixed width of the duration cell that follows the count columns on an
|
|
1481
|
+
* aggregate row — sized for the formatter's widest common output
|
|
1482
|
+
* (`999.9ms`). Longer values overflow their row rather than truncate.
|
|
1483
|
+
*
|
|
1484
|
+
* @public
|
|
1485
|
+
*/
|
|
1486
|
+
declare const DURATION_CELL_WIDTH = 7;
|
|
1479
1487
|
/**
|
|
1480
1488
|
* Renders four colored count columns (pass ✓, fail ✗, skip ↷, timeout ⧖)
|
|
1481
|
-
* for an aggregate row.
|
|
1489
|
+
* for an aggregate row. Counts render right-aligned in fixed 4-digit cells so
|
|
1490
|
+
* columns align across rows; counts of 10,000+ overflow their row without
|
|
1491
|
+
* truncation. Zeros are dimmed; all four columns always appear.
|
|
1482
1492
|
*
|
|
1483
1493
|
* @public
|
|
1484
1494
|
*/
|
|
@@ -1591,8 +1601,13 @@ interface ProjectRowProps {
|
|
|
1591
1601
|
readonly frame: string;
|
|
1592
1602
|
/** Name-column width so the count columns align across rows. */
|
|
1593
1603
|
readonly nameWidth: number;
|
|
1594
|
-
/** Per-tag test counts for the project, merged across its modules. */
|
|
1595
|
-
readonly tagCounts?: Record<string, number
|
|
1604
|
+
/** Per-tag test counts for the project, merged across its modules; feeds the TagColumns cells. */
|
|
1605
|
+
readonly tagCounts?: Record<string, number> | undefined;
|
|
1606
|
+
/**
|
|
1607
|
+
* The view-level tag union. Every row renders every tag in it so the
|
|
1608
|
+
* tag columns align; empty (the default) renders no tag cells.
|
|
1609
|
+
*/
|
|
1610
|
+
readonly tagUnion?: ReadonlyArray<string>;
|
|
1596
1611
|
}
|
|
1597
1612
|
/**
|
|
1598
1613
|
* Renders one per-project row in the `workspace`-shape live view: status
|
|
@@ -1712,6 +1727,35 @@ declare const spinnerFrame: (index: number) => string;
|
|
|
1712
1727
|
*/
|
|
1713
1728
|
declare const spinnerFrameForTime: (nowMs: number) => number;
|
|
1714
1729
|
//#endregion
|
|
1730
|
+
//#region src/render-ink/TagColumns.d.ts
|
|
1731
|
+
/**
|
|
1732
|
+
* Compute the view-level tag union: the alphabetically sorted set of tag
|
|
1733
|
+
* names across every row's tag counts. A union of a single tag carries no
|
|
1734
|
+
* signal (the whole view is one kind), so it collapses to empty and the
|
|
1735
|
+
* view renders no tag columns at all.
|
|
1736
|
+
*
|
|
1737
|
+
* @public
|
|
1738
|
+
*/
|
|
1739
|
+
declare const tagUnion: (rows: ReadonlyArray<Record<string, number> | undefined>) => ReadonlyArray<string>;
|
|
1740
|
+
/**
|
|
1741
|
+
* Props for the `TagColumns` component.
|
|
1742
|
+
*
|
|
1743
|
+
* @public
|
|
1744
|
+
*/
|
|
1745
|
+
interface TagColumnsProps {
|
|
1746
|
+
/** The view-level tag union — every row renders every tag in it. */
|
|
1747
|
+
readonly tags: ReadonlyArray<string>;
|
|
1748
|
+
/** This row's per-tag test counts; tags absent here render a dimmed 0. */
|
|
1749
|
+
readonly counts?: Record<string, number> | undefined;
|
|
1750
|
+
}
|
|
1751
|
+
/**
|
|
1752
|
+
* Renders one row's fixed-width tag cells for the given tag union.
|
|
1753
|
+
* Renders nothing when the union is empty.
|
|
1754
|
+
*
|
|
1755
|
+
* @public
|
|
1756
|
+
*/
|
|
1757
|
+
declare const TagColumns: FC<TagColumnsProps>;
|
|
1758
|
+
//#endregion
|
|
1715
1759
|
//#region src/render-ink/TestRow.d.ts
|
|
1716
1760
|
/**
|
|
1717
1761
|
* Props for the `TestRow` component.
|
|
@@ -1845,5 +1889,5 @@ declare const synthesizeFromAgentReport: (report: AgentReport, options?: Synthes
|
|
|
1845
1889
|
*/
|
|
1846
1890
|
declare const CURRENT_UI_VERSION: string;
|
|
1847
1891
|
//#endregion
|
|
1848
|
-
export { ActionSeverity, type AgentCellFn, type AgentReport, CURRENT_UI_VERSION, type Cell, type CellOptions, CountColumns, type CountColumnsProps, CoverageBlock, type CoverageBlockProps, FileCoverageReport as CoverageFile, CoverageGap, CoverageMetric, CoverageRenderState, type CoverageTotals, type DispatchInputs, FailureRecord, FailureSection, type FailureSectionProps, FailuresSection, type FailuresSectionProps, type InkCellFn, type MetricThresholds, ModuleHeader, type ModuleHeaderProps, ModuleRecord, ProjectRow, type ProjectRowProps, type ProjectSummary, type RenderAgentOptions, RenderState, RenderTotals, RunEvent, type RunEventByTag, RunEventChannel, RunEventChannelLive, type RunOutcome, type RunShape, SPINNER_FRAMES, SPINNER_FRAME_MS, StatusIcon, type StatusIconKind, type StatusIconProps, StreamApp, type StreamAppProps, SuggestedActionRecord, SuggestedActions, type SuggestedActionsProps, type SynthesizeFromAgentReportOptions, type SynthesizeOptions, type SynthesizedCoverage, type TestClassification, TestRecord, TestRow, type TestRowProps, TrendLine, type TrendLineProps, type TrendSummary, type VitestModuleDiagnostic, type VitestModuleError, type VitestParsedStack, type VitestTestCase, type VitestTestDiagnostic, type VitestTestError, type VitestTestModule, type VitestTestResult, type VitestTestSuite, accumulateUntilFinished, buildFooter, classifyOutcome, classifyRunShape, dispatch, dispatchInk, dispatcherTable, dominantClassification, forEachRenderState, formatDisplayDuration, initialRenderState, publish, publishAll, reduceRenderState, reduceRenderStateAll, renderAgent, renderStateStream, spinnerFrame, spinnerFrameForTime, subscribeRaw, synthesizeFromAgentReport, synthesizeRunEvents };
|
|
1892
|
+
export { ActionSeverity, type AgentCellFn, type AgentReport, CURRENT_UI_VERSION, type Cell, type CellOptions, CountColumns, type CountColumnsProps, CoverageBlock, type CoverageBlockProps, FileCoverageReport as CoverageFile, CoverageGap, CoverageMetric, CoverageRenderState, type CoverageTotals, DURATION_CELL_WIDTH, type DispatchInputs, FailureRecord, FailureSection, type FailureSectionProps, FailuresSection, type FailuresSectionProps, type InkCellFn, type MetricThresholds, ModuleHeader, type ModuleHeaderProps, ModuleRecord, ProjectRow, type ProjectRowProps, type ProjectSummary, type RenderAgentOptions, RenderState, RenderTotals, RunEvent, type RunEventByTag, RunEventChannel, RunEventChannelLive, type RunOutcome, type RunShape, SPINNER_FRAMES, SPINNER_FRAME_MS, StatusIcon, type StatusIconKind, type StatusIconProps, StreamApp, type StreamAppProps, SuggestedActionRecord, SuggestedActions, type SuggestedActionsProps, type SynthesizeFromAgentReportOptions, type SynthesizeOptions, type SynthesizedCoverage, TagColumns, type TagColumnsProps, type TestClassification, TestRecord, TestRow, type TestRowProps, TrendLine, type TrendLineProps, type TrendSummary, type VitestModuleDiagnostic, type VitestModuleError, type VitestParsedStack, type VitestTestCase, type VitestTestDiagnostic, type VitestTestError, type VitestTestModule, type VitestTestResult, type VitestTestSuite, accumulateUntilFinished, buildFooter, classifyOutcome, classifyRunShape, dispatch, dispatchInk, dispatcherTable, dominantClassification, forEachRenderState, formatDisplayDuration, initialRenderState, publish, publishAll, reduceRenderState, reduceRenderStateAll, renderAgent, renderStateStream, spinnerFrame, spinnerFrameForTime, subscribeRaw, synthesizeFromAgentReport, synthesizeRunEvents, tagUnion };
|
|
1849
1893
|
//# sourceMappingURL=index.d.ts.map
|
package/index.js
CHANGED
|
@@ -8,12 +8,13 @@ import { reduceRenderState, reduceRenderStateAll } from "./reducer.js";
|
|
|
8
8
|
import { accumulateUntilFinished, forEachRenderState, renderStateStream, subscribeRaw } from "./pubsub/Subscriber.js";
|
|
9
9
|
import "./pubsub/index.js";
|
|
10
10
|
import { renderAgent } from "./render-agent.js";
|
|
11
|
-
import { CountColumns } from "./render-ink/CountColumns.js";
|
|
11
|
+
import { CountColumns, DURATION_CELL_WIDTH } from "./render-ink/CountColumns.js";
|
|
12
12
|
import { CoverageBlock } from "./render-ink/CoverageBlock.js";
|
|
13
13
|
import { FailureSection } from "./render-ink/FailureSection.js";
|
|
14
14
|
import { FailuresSection } from "./render-ink/FailuresSection.js";
|
|
15
15
|
import { StatusIcon } from "./render-ink/StatusIcon.js";
|
|
16
16
|
import { ModuleHeader } from "./render-ink/ModuleHeader.js";
|
|
17
|
+
import { TagColumns, tagUnion } from "./render-ink/TagColumns.js";
|
|
17
18
|
import { ProjectRow } from "./render-ink/ProjectRow.js";
|
|
18
19
|
import { SPINNER_FRAMES, SPINNER_FRAME_MS, spinnerFrame, spinnerFrameForTime } from "./render-ink/spinner.js";
|
|
19
20
|
import { TestRow } from "./render-ink/TestRow.js";
|
|
@@ -32,7 +33,7 @@ import { ActionSeverity, CoverageFile, CoverageGap, CoverageMetric, CoverageRend
|
|
|
32
33
|
*
|
|
33
34
|
* @public
|
|
34
35
|
*/
|
|
35
|
-
const CURRENT_UI_VERSION = "1.0
|
|
36
|
+
const CURRENT_UI_VERSION = "1.1.0";
|
|
36
37
|
|
|
37
38
|
//#endregion
|
|
38
|
-
export { ActionSeverity, CURRENT_UI_VERSION, CountColumns, CoverageBlock, CoverageFile, CoverageGap, CoverageMetric, CoverageRenderState, FailureRecord, FailureSection, FailuresSection, ModuleHeader, ModuleRecord, ProjectRow, RenderState, RenderTotals, RunEvent, RunEventChannel, RunEventChannelLive, SPINNER_FRAMES, SPINNER_FRAME_MS, StatusIcon, StreamApp, SuggestedActionRecord, SuggestedActions, TestRecord, TestRow, TrendLine, accumulateUntilFinished, buildFooter, classifyOutcome, classifyRunShape, dispatch, dispatchInk, dispatcherTable, dominantClassification, forEachRenderState, formatDisplayDuration, initialRenderState, publish, publishAll, reduceRenderState, reduceRenderStateAll, renderAgent, renderStateStream, spinnerFrame, spinnerFrameForTime, subscribeRaw, synthesizeFromAgentReport, synthesizeRunEvents };
|
|
39
|
+
export { ActionSeverity, CURRENT_UI_VERSION, CountColumns, CoverageBlock, CoverageFile, CoverageGap, CoverageMetric, CoverageRenderState, DURATION_CELL_WIDTH, FailureRecord, FailureSection, FailuresSection, ModuleHeader, ModuleRecord, ProjectRow, RenderState, RenderTotals, RunEvent, RunEventChannel, RunEventChannelLive, SPINNER_FRAMES, SPINNER_FRAME_MS, StatusIcon, StreamApp, SuggestedActionRecord, SuggestedActions, TagColumns, TestRecord, TestRow, TrendLine, accumulateUntilFinished, buildFooter, classifyOutcome, classifyRunShape, dispatch, dispatchInk, dispatcherTable, dominantClassification, forEachRenderState, formatDisplayDuration, initialRenderState, publish, publishAll, reduceRenderState, reduceRenderStateAll, renderAgent, renderStateStream, spinnerFrame, spinnerFrameForTime, subscribeRaw, synthesizeFromAgentReport, synthesizeRunEvents, tagUnion };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vitest-agent/ui",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Shared event-sourced renderer for vitest-agent. React Ink view for humans, plain-string view for agents, both fed by the same reducer over a RunEvent stream.",
|
|
6
6
|
"keywords": [
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"./package.json": "./package.json"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@vitest-agent/sdk": "1.3.
|
|
41
|
+
"@vitest-agent/sdk": "1.3.2",
|
|
42
42
|
"effect": "^3.21.4"
|
|
43
43
|
},
|
|
44
44
|
"peerDependencies": {
|
|
@@ -7,16 +7,23 @@ import { jsxs } from "react/jsx-runtime";
|
|
|
7
7
|
* module): pass, fail, skip, timeout. All four always render — a zero
|
|
8
8
|
* is dimmed, never omitted — so the columns line up across rows.
|
|
9
9
|
*/
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
10
|
+
/**
|
|
11
|
+
* Fixed width of the duration cell that follows the count columns on an
|
|
12
|
+
* aggregate row — sized for the formatter's widest common output
|
|
13
|
+
* (`999.9ms`). Longer values overflow their row rather than truncate.
|
|
14
|
+
*
|
|
15
|
+
* @public
|
|
16
|
+
*/
|
|
17
|
+
const DURATION_CELL_WIDTH = 7;
|
|
18
|
+
const cell = (count, glyph, color) => /* @__PURE__ */ jsxs(Text, {
|
|
19
|
+
color: count > 0 ? color : "gray",
|
|
20
|
+
children: [String(count).padStart(4), glyph]
|
|
16
21
|
});
|
|
17
22
|
/**
|
|
18
23
|
* Renders four colored count columns (pass ✓, fail ✗, skip ↷, timeout ⧖)
|
|
19
|
-
* for an aggregate row.
|
|
24
|
+
* for an aggregate row. Counts render right-aligned in fixed 4-digit cells so
|
|
25
|
+
* columns align across rows; counts of 10,000+ overflow their row without
|
|
26
|
+
* truncation. Zeros are dimmed; all four columns always appear.
|
|
20
27
|
*
|
|
21
28
|
* @public
|
|
22
29
|
*/
|
|
@@ -31,4 +38,4 @@ const CountColumns = ({ passCount, failCount, skipCount, timeoutCount }) => /* @
|
|
|
31
38
|
] });
|
|
32
39
|
|
|
33
40
|
//#endregion
|
|
34
|
-
export { CountColumns };
|
|
41
|
+
export { CountColumns, DURATION_CELL_WIDTH };
|
package/render-ink/ProjectRow.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { formatDisplayDuration } from "../format-duration.js";
|
|
2
|
-
import { CountColumns } from "./CountColumns.js";
|
|
2
|
+
import { CountColumns, DURATION_CELL_WIDTH } from "./CountColumns.js";
|
|
3
3
|
import { StatusIcon } from "./StatusIcon.js";
|
|
4
|
-
import {
|
|
4
|
+
import { TagColumns } from "./TagColumns.js";
|
|
5
5
|
import { Box, Text } from "ink";
|
|
6
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
7
7
|
|
|
8
8
|
//#region src/render-ink/ProjectRow.tsx
|
|
9
9
|
/**
|
|
@@ -28,7 +28,7 @@ const projectGlyph = (counts, running, timedOut, frame) => {
|
|
|
28
28
|
*
|
|
29
29
|
* @public
|
|
30
30
|
*/
|
|
31
|
-
const ProjectRow = ({ project, counts, running, timedOut = false, elapsedMs, frame, nameWidth, tagCounts }) => /* @__PURE__ */ jsxs(Box, { children: [
|
|
31
|
+
const ProjectRow = ({ project, counts, running, timedOut = false, elapsedMs, frame, nameWidth, tagCounts, tagUnion = [] }) => /* @__PURE__ */ jsxs(Box, { children: [
|
|
32
32
|
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
33
33
|
projectGlyph(counts, running, timedOut, frame),
|
|
34
34
|
/* @__PURE__ */ jsxs(Text, { children: [
|
|
@@ -44,12 +44,12 @@ const ProjectRow = ({ project, counts, running, timedOut = false, elapsedMs, fra
|
|
|
44
44
|
}),
|
|
45
45
|
/* @__PURE__ */ jsxs(Text, {
|
|
46
46
|
dimColor: true,
|
|
47
|
-
children: [" ", formatDisplayDuration(elapsedMs)]
|
|
47
|
+
children: [" ", formatDisplayDuration(elapsedMs).padStart(7)]
|
|
48
48
|
}),
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}) : null
|
|
49
|
+
tagUnion.length > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Text, { children: " " }), /* @__PURE__ */ jsx(TagColumns, {
|
|
50
|
+
tags: tagUnion,
|
|
51
|
+
counts: tagCounts
|
|
52
|
+
})] }) : null
|
|
53
53
|
] });
|
|
54
54
|
|
|
55
55
|
//#endregion
|
package/render-ink/StreamApp.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { classifyRunShape } from "../dispatcher/classify.js";
|
|
2
2
|
import { formatDisplayDuration } from "../format-duration.js";
|
|
3
|
-
import { CountColumns } from "./CountColumns.js";
|
|
3
|
+
import { CountColumns, DURATION_CELL_WIDTH } from "./CountColumns.js";
|
|
4
4
|
import { StatusIcon } from "./StatusIcon.js";
|
|
5
|
-
import {
|
|
5
|
+
import { TagColumns, tagUnion } from "./TagColumns.js";
|
|
6
6
|
import { ProjectRow } from "./ProjectRow.js";
|
|
7
7
|
import { spinnerFrame } from "./spinner.js";
|
|
8
8
|
import { TestRow } from "./TestRow.js";
|
|
@@ -89,7 +89,7 @@ const moduleIcon = (m) => {
|
|
|
89
89
|
* Used in the Live region for both running rows (spinner, `running=true`)
|
|
90
90
|
* and finished rows (resolved glyph, `running=false`).
|
|
91
91
|
*/
|
|
92
|
-
const ModuleStreamRow = ({ module, nowMs, frame, nameWidth, timedOut }) => {
|
|
92
|
+
const ModuleStreamRow = ({ module, nowMs, frame, nameWidth, timedOut, tagUnion: rowTagUnion }) => {
|
|
93
93
|
const running = moduleRunning(module);
|
|
94
94
|
const queued = module.status === "queued";
|
|
95
95
|
let glyph;
|
|
@@ -122,12 +122,12 @@ const ModuleStreamRow = ({ module, nowMs, frame, nameWidth, timedOut }) => {
|
|
|
122
122
|
}),
|
|
123
123
|
/* @__PURE__ */ jsxs(Text, {
|
|
124
124
|
dimColor: true,
|
|
125
|
-
children: [" ", formatDisplayDuration(moduleElapsedMs(module, nowMs))]
|
|
125
|
+
children: [" ", formatDisplayDuration(moduleElapsedMs(module, nowMs)).padStart(7)]
|
|
126
126
|
}),
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
}) : null,
|
|
127
|
+
rowTagUnion.length > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Text, { children: " " }), /* @__PURE__ */ jsx(TagColumns, {
|
|
128
|
+
tags: rowTagUnion,
|
|
129
|
+
counts: module.tagCounts
|
|
130
|
+
})] }) : null,
|
|
131
131
|
notStartedSuffix
|
|
132
132
|
] });
|
|
133
133
|
};
|
|
@@ -192,10 +192,15 @@ const FailureItem = ({ failure }) => /* @__PURE__ */ jsxs(Box, {
|
|
|
192
192
|
}) : null
|
|
193
193
|
] }), /* @__PURE__ */ jsx(InlineError, { failure })]
|
|
194
194
|
});
|
|
195
|
-
|
|
195
|
+
/**
|
|
196
|
+
* The bottom `Total:` rollup line. In the workspace view `labelWidth`
|
|
197
|
+
* pads the label to the project-row prefix width so the count columns
|
|
198
|
+
* align with the rows above; other shapes omit it.
|
|
199
|
+
*/
|
|
200
|
+
const TotalsLine = ({ totals, labelWidth }) => /* @__PURE__ */ jsxs(Text, { children: [
|
|
196
201
|
/* @__PURE__ */ jsx(Text, {
|
|
197
202
|
bold: true,
|
|
198
|
-
children: "Total:"
|
|
203
|
+
children: labelWidth !== void 0 ? "Total:".padEnd(labelWidth) : "Total:"
|
|
199
204
|
}),
|
|
200
205
|
" ",
|
|
201
206
|
/* @__PURE__ */ jsx(CountColumns, {
|
|
@@ -206,7 +211,7 @@ const TotalsLine = ({ totals }) => /* @__PURE__ */ jsxs(Text, { children: [
|
|
|
206
211
|
}),
|
|
207
212
|
/* @__PURE__ */ jsxs(Text, {
|
|
208
213
|
dimColor: true,
|
|
209
|
-
children: [" ", formatDisplayDuration(totals.durationMs)]
|
|
214
|
+
children: [" ", formatDisplayDuration(totals.durationMs).padStart(7)]
|
|
210
215
|
})
|
|
211
216
|
] });
|
|
212
217
|
const CoverageItem = ({ state }) => {
|
|
@@ -269,16 +274,21 @@ const liveRegion = (state, shape, nowMs, frame) => {
|
|
|
269
274
|
}) : null;
|
|
270
275
|
const coverageItem = /* @__PURE__ */ jsx(CoverageItem, { state });
|
|
271
276
|
const trendItem = state.trend !== null ? /* @__PURE__ */ jsx(TrendLine, { trend: state.trend }) : null;
|
|
272
|
-
const totalsItem = /* @__PURE__ */ jsxs(Box, {
|
|
277
|
+
const totalsItem = (labelWidth) => /* @__PURE__ */ jsxs(Box, {
|
|
273
278
|
flexDirection: "column",
|
|
274
279
|
children: [finished && timedOut ? /* @__PURE__ */ jsx(Text, {
|
|
275
280
|
color: "#e09a4e",
|
|
276
281
|
bold: true,
|
|
277
282
|
children: "⧖ Run timed out"
|
|
278
|
-
}) : null, /* @__PURE__ */ jsx(TotalsLine, {
|
|
283
|
+
}) : null, /* @__PURE__ */ jsx(TotalsLine, {
|
|
284
|
+
totals: state.totals,
|
|
285
|
+
labelWidth
|
|
286
|
+
})]
|
|
279
287
|
});
|
|
280
288
|
if (shape === "workspace") {
|
|
281
289
|
const nameWidth = Math.max(0, ...groups.map((g) => g.name.length));
|
|
290
|
+
const groupTagCounts = new Map(groups.map((g) => [g.name, mergeTagCounts(g.modules)]));
|
|
291
|
+
const workspaceTags = tagUnion(groups.map((g) => groupTagCounts.get(g.name)));
|
|
282
292
|
let runningVisible = 0;
|
|
283
293
|
let runningOverflow = 0;
|
|
284
294
|
const rows = [];
|
|
@@ -300,7 +310,8 @@ const liveRegion = (state, shape, nowMs, frame) => {
|
|
|
300
310
|
elapsedMs: projectElapsedMs(g, nowMs),
|
|
301
311
|
frame,
|
|
302
312
|
nameWidth,
|
|
303
|
-
tagCounts:
|
|
313
|
+
tagCounts: groupTagCounts.get(g.name),
|
|
314
|
+
tagUnion: workspaceTags
|
|
304
315
|
}, g.name));
|
|
305
316
|
} else runningOverflow++;
|
|
306
317
|
else {
|
|
@@ -320,7 +331,8 @@ const liveRegion = (state, shape, nowMs, frame) => {
|
|
|
320
331
|
elapsedMs: projectElapsedMs(g, nowMs),
|
|
321
332
|
frame,
|
|
322
333
|
nameWidth,
|
|
323
|
-
tagCounts:
|
|
334
|
+
tagCounts: groupTagCounts.get(g.name),
|
|
335
|
+
tagUnion: workspaceTags
|
|
324
336
|
}, g.name));
|
|
325
337
|
}
|
|
326
338
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
@@ -344,11 +356,12 @@ const liveRegion = (state, shape, nowMs, frame) => {
|
|
|
344
356
|
failuresSection,
|
|
345
357
|
coverageItem,
|
|
346
358
|
trendItem,
|
|
347
|
-
totalsItem
|
|
359
|
+
totalsItem(nameWidth + 4)
|
|
348
360
|
] });
|
|
349
361
|
}
|
|
350
362
|
if (shape === "single-project") {
|
|
351
363
|
const nameWidth = Math.max(0, ...ordered.map((m) => m.modulePath.length));
|
|
364
|
+
const moduleTags = tagUnion(ordered.map((m) => m.tagCounts));
|
|
352
365
|
let runningVisible = 0;
|
|
353
366
|
let runningOverflow = 0;
|
|
354
367
|
const rows = [];
|
|
@@ -359,7 +372,8 @@ const liveRegion = (state, shape, nowMs, frame) => {
|
|
|
359
372
|
nowMs,
|
|
360
373
|
frame,
|
|
361
374
|
nameWidth,
|
|
362
|
-
timedOut
|
|
375
|
+
timedOut,
|
|
376
|
+
tagUnion: moduleTags
|
|
363
377
|
}, m.modulePath));
|
|
364
378
|
} else runningOverflow++;
|
|
365
379
|
else rows.push(/* @__PURE__ */ jsx(ModuleStreamRow, {
|
|
@@ -367,7 +381,8 @@ const liveRegion = (state, shape, nowMs, frame) => {
|
|
|
367
381
|
nowMs,
|
|
368
382
|
frame,
|
|
369
383
|
nameWidth,
|
|
370
|
-
timedOut
|
|
384
|
+
timedOut,
|
|
385
|
+
tagUnion: moduleTags
|
|
371
386
|
}, m.modulePath));
|
|
372
387
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
373
388
|
/* @__PURE__ */ jsxs(Text, {
|
|
@@ -390,7 +405,7 @@ const liveRegion = (state, shape, nowMs, frame) => {
|
|
|
390
405
|
failuresSection,
|
|
391
406
|
coverageItem,
|
|
392
407
|
trendItem,
|
|
393
|
-
totalsItem
|
|
408
|
+
totalsItem()
|
|
394
409
|
] });
|
|
395
410
|
}
|
|
396
411
|
const sole = ordered[0];
|
|
@@ -422,7 +437,7 @@ const liveRegion = (state, shape, nowMs, frame) => {
|
|
|
422
437
|
}),
|
|
423
438
|
coverageItem,
|
|
424
439
|
trendItem,
|
|
425
|
-
totalsItem
|
|
440
|
+
totalsItem()
|
|
426
441
|
] });
|
|
427
442
|
};
|
|
428
443
|
/**
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { Text } from "ink";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
|
|
4
|
+
//#region src/render-ink/TagColumns.tsx
|
|
5
|
+
/**
|
|
6
|
+
* Fixed-width tag-count columns for aggregate rows in the `stream` view.
|
|
7
|
+
* Every row renders every tag in the view's union so the columns align
|
|
8
|
+
* across rows; a zero count is dimmed gray, a nonzero count cyan. Each
|
|
9
|
+
* cell is the tag label, a colon, and a 4-digit right-aligned count;
|
|
10
|
+
* cells sit two spaces apart.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Compute the view-level tag union: the alphabetically sorted set of tag
|
|
14
|
+
* names across every row's tag counts. A union of a single tag carries no
|
|
15
|
+
* signal (the whole view is one kind), so it collapses to empty and the
|
|
16
|
+
* view renders no tag columns at all.
|
|
17
|
+
*
|
|
18
|
+
* @public
|
|
19
|
+
*/
|
|
20
|
+
const tagUnion = (rows) => {
|
|
21
|
+
const names = /* @__PURE__ */ new Set();
|
|
22
|
+
for (const row of rows) for (const tag of Object.keys(row ?? {})) names.add(tag);
|
|
23
|
+
if (names.size <= 1) return [];
|
|
24
|
+
return [...names].sort((a, b) => a.localeCompare(b));
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Renders one row's fixed-width tag cells for the given tag union.
|
|
28
|
+
* Renders nothing when the union is empty.
|
|
29
|
+
*
|
|
30
|
+
* @public
|
|
31
|
+
*/
|
|
32
|
+
const TagColumns = ({ tags, counts }) => {
|
|
33
|
+
if (tags.length === 0) return null;
|
|
34
|
+
return /* @__PURE__ */ jsx(Text, { children: tags.map((tag, i) => {
|
|
35
|
+
const count = counts?.[tag] ?? 0;
|
|
36
|
+
return /* @__PURE__ */ jsxs(Text, {
|
|
37
|
+
color: count > 0 ? "cyan" : "gray",
|
|
38
|
+
children: [
|
|
39
|
+
i > 0 ? " " : "",
|
|
40
|
+
tag,
|
|
41
|
+
":",
|
|
42
|
+
String(count).padStart(4)
|
|
43
|
+
]
|
|
44
|
+
}, tag);
|
|
45
|
+
}) });
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
//#endregion
|
|
49
|
+
export { TagColumns, tagUnion };
|
package/render-ink/index.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { CountColumns } from "./CountColumns.js";
|
|
1
|
+
import { CountColumns, DURATION_CELL_WIDTH } from "./CountColumns.js";
|
|
2
2
|
import { CoverageBlock } from "./CoverageBlock.js";
|
|
3
3
|
import { FailureSection } from "./FailureSection.js";
|
|
4
4
|
import { FailuresSection } from "./FailuresSection.js";
|
|
5
5
|
import { StatusIcon } from "./StatusIcon.js";
|
|
6
6
|
import { ModuleHeader } from "./ModuleHeader.js";
|
|
7
|
+
import { TagColumns, tagUnion } from "./TagColumns.js";
|
|
7
8
|
import { ProjectRow } from "./ProjectRow.js";
|
|
8
9
|
import { SPINNER_FRAMES, SPINNER_FRAME_MS, spinnerFrame, spinnerFrameForTime } from "./spinner.js";
|
|
9
10
|
import { TestRow } from "./TestRow.js";
|
|
@@ -11,4 +12,4 @@ import { TrendLine } from "./TrendLine.js";
|
|
|
11
12
|
import { StreamApp } from "./StreamApp.js";
|
|
12
13
|
import { SuggestedActions } from "./SuggestedActions.js";
|
|
13
14
|
|
|
14
|
-
export { CountColumns, CoverageBlock, FailureSection, FailuresSection, ModuleHeader, ProjectRow, SPINNER_FRAMES, SPINNER_FRAME_MS, StatusIcon, StreamApp, SuggestedActions, TestRow, TrendLine, spinnerFrame, spinnerFrameForTime };
|
|
15
|
+
export { CountColumns, CoverageBlock, DURATION_CELL_WIDTH, FailureSection, FailuresSection, ModuleHeader, ProjectRow, SPINNER_FRAMES, SPINNER_FRAME_MS, StatusIcon, StreamApp, SuggestedActions, TagColumns, TestRow, TrendLine, spinnerFrame, spinnerFrameForTime, tagUnion };
|
package/render-ink/tag-suffix.js
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
//#region src/render-ink/tag-suffix.ts
|
|
2
|
-
/**
|
|
3
|
-
* Format a per-row tag-count suffix — sorted `tag:count` pairs joined
|
|
4
|
-
* by two spaces. A single tag is suppressed: a project that is all one
|
|
5
|
-
* kind carries no signal worth the column, matching the agent
|
|
6
|
-
* renderer's behaviour.
|
|
7
|
-
*
|
|
8
|
-
* @packageDocumentation
|
|
9
|
-
*/
|
|
10
|
-
const formatTagSuffix = (tagCounts) => {
|
|
11
|
-
if (tagCounts === void 0) return "";
|
|
12
|
-
const entries = Object.entries(tagCounts);
|
|
13
|
-
if (entries.length <= 1) return "";
|
|
14
|
-
return [...entries].sort(([a], [b]) => a.localeCompare(b)).map(([tag, count]) => `${tag}:${count}`).join(" ");
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
//#endregion
|
|
18
|
-
export { formatTagSuffix };
|