@vitest-agent/ui 1.0.5 → 1.1.1
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 +59 -53
- 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
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { Context, Effect, Layer, PubSub, Queue, Schema, Scope, Stream } from "effect";
|
|
2
|
+
import "@effect/platform";
|
|
3
|
+
import "config-file-effect";
|
|
4
|
+
import "@effect/sql/SqlClient";
|
|
5
|
+
import "workspaces-effect";
|
|
6
|
+
import "xdg-effect";
|
|
2
7
|
import { FC, ReactElement } from "react";
|
|
3
|
-
|
|
4
8
|
//#region ../sdk/dist/dev/pkg/index.d.ts
|
|
5
9
|
//#region src/schemas/Coverage.d.ts
|
|
6
10
|
/**
|
|
@@ -31,10 +35,6 @@ declare const FileCoverageReport: Schema.Struct<{
|
|
|
31
35
|
}>;
|
|
32
36
|
/** @public */
|
|
33
37
|
type FileCoverageReport = typeof FileCoverageReport.Type;
|
|
34
|
-
/**
|
|
35
|
-
* Complete coverage report attached to an AgentReport.
|
|
36
|
-
* @public
|
|
37
|
-
*/
|
|
38
38
|
//#endregion
|
|
39
39
|
//#region src/schemas/RenderState.d.ts
|
|
40
40
|
/**
|
|
@@ -306,7 +306,8 @@ type RenderState = typeof RenderState.Type;
|
|
|
306
306
|
* code share one definition.
|
|
307
307
|
* @public
|
|
308
308
|
*/
|
|
309
|
-
declare const initialRenderState: RenderState;
|
|
309
|
+
declare const initialRenderState: RenderState;
|
|
310
|
+
//#endregion
|
|
310
311
|
//#region src/contracts/dispatcher.d.ts
|
|
311
312
|
/**
|
|
312
313
|
* The four run shapes the dispatcher distinguishes.
|
|
@@ -427,12 +428,7 @@ interface CellOptions {
|
|
|
427
428
|
* cells can call this directly without re-consulting the environment.
|
|
428
429
|
*/
|
|
429
430
|
readonly osc8: (url: string, label: string) => string;
|
|
430
|
-
}
|
|
431
|
-
//#region src/schemas/AgentReport.d.ts
|
|
432
|
-
/**
|
|
433
|
-
* Aggregate test run statistics.
|
|
434
|
-
* @public
|
|
435
|
-
*/
|
|
431
|
+
}
|
|
436
432
|
/**
|
|
437
433
|
* Complete per-project test report written to disk as JSON.
|
|
438
434
|
* @public
|
|
@@ -584,12 +580,7 @@ declare const AgentReport: Schema.Struct<{
|
|
|
584
580
|
}>>;
|
|
585
581
|
}>;
|
|
586
582
|
/** @public */
|
|
587
|
-
type AgentReport = typeof AgentReport.Type;
|
|
588
|
-
//#region src/schemas/Common.d.ts
|
|
589
|
-
/**
|
|
590
|
-
* Possible states for an individual test case.
|
|
591
|
-
* @public
|
|
592
|
-
*/
|
|
583
|
+
type AgentReport = typeof AgentReport.Type;
|
|
593
584
|
/**
|
|
594
585
|
* Classification of a test's failure history across runs.
|
|
595
586
|
* @public
|
|
@@ -597,10 +588,6 @@ type AgentReport = typeof AgentReport.Type; //#endregion
|
|
|
597
588
|
declare const TestClassification: Schema.Literal<["stable", "new-failure", "persistent", "flaky", "recovered"]>;
|
|
598
589
|
/** @public */
|
|
599
590
|
type TestClassification = typeof TestClassification.Type;
|
|
600
|
-
/**
|
|
601
|
-
* Console output verbosity mode for AgentReporter.
|
|
602
|
-
* @public
|
|
603
|
-
*/
|
|
604
591
|
//#endregion
|
|
605
592
|
//#region src/schemas/RunEvent.d.ts
|
|
606
593
|
/**
|
|
@@ -617,10 +604,6 @@ type CoverageMetric = typeof CoverageMetric.Type;
|
|
|
617
604
|
declare const ActionSeverity: Schema.Literal<["info", "warn", "blocker"]>;
|
|
618
605
|
/** @public */
|
|
619
606
|
type ActionSeverity = typeof ActionSeverity.Type;
|
|
620
|
-
/**
|
|
621
|
-
* Lifecycle scope of a Vitest setup/teardown hook.
|
|
622
|
-
* @public
|
|
623
|
-
*/
|
|
624
607
|
/**
|
|
625
608
|
* A coverage gap surfaced to the renderer — a per-file shortfall that
|
|
626
609
|
* a renderer may want to call out individually.
|
|
@@ -796,7 +779,8 @@ type RunEvent = typeof RunEvent.Type;
|
|
|
796
779
|
* pulling the whole union shape into scope.
|
|
797
780
|
* @public
|
|
798
781
|
*/
|
|
799
|
-
type RunEventByTag = { [E in RunEvent as E["_tag"]]: E };
|
|
782
|
+
type RunEventByTag = { [E in RunEvent as E["_tag"]]: E; };
|
|
783
|
+
//#endregion
|
|
800
784
|
//#region src/schemas/Thresholds.d.ts
|
|
801
785
|
/**
|
|
802
786
|
* Per-metric threshold values. All optional -- only set metrics are enforced.
|
|
@@ -810,10 +794,6 @@ declare const MetricThresholds: Schema.Struct<{
|
|
|
810
794
|
}>;
|
|
811
795
|
/** @public */
|
|
812
796
|
type MetricThresholds = typeof MetricThresholds.Type;
|
|
813
|
-
/**
|
|
814
|
-
* A glob pattern paired with its metric thresholds.
|
|
815
|
-
* @public
|
|
816
|
-
*/
|
|
817
797
|
//#endregion
|
|
818
798
|
//#region src/utils/build-report.d.ts
|
|
819
799
|
/**
|
|
@@ -952,24 +932,6 @@ interface VitestTestModule {
|
|
|
952
932
|
diagnostic(): VitestModuleDiagnostic | undefined;
|
|
953
933
|
errors(): Array<VitestModuleError>;
|
|
954
934
|
}
|
|
955
|
-
/**
|
|
956
|
-
* Convert Vitest TestModule/TestCase objects into an `AgentReport`.
|
|
957
|
-
*
|
|
958
|
-
* @remarks
|
|
959
|
-
* This is a pure data transformation function with no I/O. It tallies
|
|
960
|
-
* pass/fail/skip counts, extracts error details with diffs, and builds
|
|
961
|
-
* the complete report structure. Only modules with at least one failing
|
|
962
|
-
* test are included in the `failed` array to keep reports compact.
|
|
963
|
-
*
|
|
964
|
-
* @param testModules - All test modules from the Vitest run
|
|
965
|
-
* @param unhandledErrors - Unhandled errors from the run
|
|
966
|
-
* @param reason - Overall run outcome (`"passed"`, `"failed"`, or `"interrupted"`)
|
|
967
|
-
* @param options - Reporter options; only `omitPassingTests` is used
|
|
968
|
-
* @param projectName - Optional project name for monorepo grouping
|
|
969
|
-
* @returns Structured report ready for JSON serialization
|
|
970
|
-
*
|
|
971
|
-
* @public
|
|
972
|
-
*/
|
|
973
935
|
//#endregion
|
|
974
936
|
//#region src/dispatcher/cell-types.d.ts
|
|
975
937
|
/**
|
|
@@ -1476,9 +1438,19 @@ interface CountColumnsProps {
|
|
|
1476
1438
|
/** Number of timed-out tests. */
|
|
1477
1439
|
readonly timeoutCount: number;
|
|
1478
1440
|
}
|
|
1441
|
+
/**
|
|
1442
|
+
* Fixed width of the duration cell that follows the count columns on an
|
|
1443
|
+
* aggregate row — sized for the formatter's widest common output
|
|
1444
|
+
* (`999.9ms`). Longer values overflow their row rather than truncate.
|
|
1445
|
+
*
|
|
1446
|
+
* @public
|
|
1447
|
+
*/
|
|
1448
|
+
declare const DURATION_CELL_WIDTH = 7;
|
|
1479
1449
|
/**
|
|
1480
1450
|
* Renders four colored count columns (pass ✓, fail ✗, skip ↷, timeout ⧖)
|
|
1481
|
-
* for an aggregate row.
|
|
1451
|
+
* for an aggregate row. Counts render right-aligned in fixed 4-digit cells so
|
|
1452
|
+
* columns align across rows; counts of 10,000+ overflow their row without
|
|
1453
|
+
* truncation. Zeros are dimmed; all four columns always appear.
|
|
1482
1454
|
*
|
|
1483
1455
|
* @public
|
|
1484
1456
|
*/
|
|
@@ -1591,8 +1563,13 @@ interface ProjectRowProps {
|
|
|
1591
1563
|
readonly frame: string;
|
|
1592
1564
|
/** Name-column width so the count columns align across rows. */
|
|
1593
1565
|
readonly nameWidth: number;
|
|
1594
|
-
/** Per-tag test counts for the project, merged across its modules. */
|
|
1595
|
-
readonly tagCounts?: Record<string, number
|
|
1566
|
+
/** Per-tag test counts for the project, merged across its modules; feeds the TagColumns cells. */
|
|
1567
|
+
readonly tagCounts?: Record<string, number> | undefined;
|
|
1568
|
+
/**
|
|
1569
|
+
* The view-level tag union. Every row renders every tag in it so the
|
|
1570
|
+
* tag columns align; empty (the default) renders no tag cells.
|
|
1571
|
+
*/
|
|
1572
|
+
readonly tagUnion?: ReadonlyArray<string>;
|
|
1596
1573
|
}
|
|
1597
1574
|
/**
|
|
1598
1575
|
* Renders one per-project row in the `workspace`-shape live view: status
|
|
@@ -1712,6 +1689,35 @@ declare const spinnerFrame: (index: number) => string;
|
|
|
1712
1689
|
*/
|
|
1713
1690
|
declare const spinnerFrameForTime: (nowMs: number) => number;
|
|
1714
1691
|
//#endregion
|
|
1692
|
+
//#region src/render-ink/TagColumns.d.ts
|
|
1693
|
+
/**
|
|
1694
|
+
* Compute the view-level tag union: the alphabetically sorted set of tag
|
|
1695
|
+
* names across every row's tag counts. A union of a single tag carries no
|
|
1696
|
+
* signal (the whole view is one kind), so it collapses to empty and the
|
|
1697
|
+
* view renders no tag columns at all.
|
|
1698
|
+
*
|
|
1699
|
+
* @public
|
|
1700
|
+
*/
|
|
1701
|
+
declare const tagUnion: (rows: ReadonlyArray<Record<string, number> | undefined>) => ReadonlyArray<string>;
|
|
1702
|
+
/**
|
|
1703
|
+
* Props for the `TagColumns` component.
|
|
1704
|
+
*
|
|
1705
|
+
* @public
|
|
1706
|
+
*/
|
|
1707
|
+
interface TagColumnsProps {
|
|
1708
|
+
/** The view-level tag union — every row renders every tag in it. */
|
|
1709
|
+
readonly tags: ReadonlyArray<string>;
|
|
1710
|
+
/** This row's per-tag test counts; tags absent here render a dimmed 0. */
|
|
1711
|
+
readonly counts?: Record<string, number> | undefined;
|
|
1712
|
+
}
|
|
1713
|
+
/**
|
|
1714
|
+
* Renders one row's fixed-width tag cells for the given tag union.
|
|
1715
|
+
* Renders nothing when the union is empty.
|
|
1716
|
+
*
|
|
1717
|
+
* @public
|
|
1718
|
+
*/
|
|
1719
|
+
declare const TagColumns: FC<TagColumnsProps>;
|
|
1720
|
+
//#endregion
|
|
1715
1721
|
//#region src/render-ink/TestRow.d.ts
|
|
1716
1722
|
/**
|
|
1717
1723
|
* Props for the `TestRow` component.
|
|
@@ -1845,5 +1851,5 @@ declare const synthesizeFromAgentReport: (report: AgentReport, options?: Synthes
|
|
|
1845
1851
|
*/
|
|
1846
1852
|
declare const CURRENT_UI_VERSION: string;
|
|
1847
1853
|
//#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 };
|
|
1854
|
+
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
1855
|
//# 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.
|
|
36
|
+
const CURRENT_UI_VERSION = "1.1.1";
|
|
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.
|
|
3
|
+
"version": "1.1.1",
|
|
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.3",
|
|
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 };
|