@osolmaz/pi-workflows 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.
- package/LICENSE +21 -0
- package/README.md +182 -0
- package/dist/extension/executor.d.ts +58 -0
- package/dist/extension/executor.js +201 -0
- package/dist/extension/executor.js.map +1 -0
- package/dist/extension/index.d.ts +17 -0
- package/dist/extension/index.js +504 -0
- package/dist/extension/index.js.map +1 -0
- package/dist/extension/widget.d.ts +21 -0
- package/dist/extension/widget.js +142 -0
- package/dist/extension/widget.js.map +1 -0
- package/dist/render/ansi.d.ts +16 -0
- package/dist/render/ansi.js +42 -0
- package/dist/render/ansi.js.map +1 -0
- package/dist/render/canvas.d.ts +40 -0
- package/dist/render/canvas.js +177 -0
- package/dist/render/canvas.js.map +1 -0
- package/dist/render/format.d.ts +3 -0
- package/dist/render/format.js +17 -0
- package/dist/render/format.js.map +1 -0
- package/dist/render/graph-render.d.ts +22 -0
- package/dist/render/graph-render.js +520 -0
- package/dist/render/graph-render.js.map +1 -0
- package/dist/render/graph.d.ts +46 -0
- package/dist/render/graph.js +272 -0
- package/dist/render/graph.js.map +1 -0
- package/dist/viewer/cli.d.ts +10 -0
- package/dist/viewer/cli.js +132 -0
- package/dist/viewer/cli.js.map +1 -0
- package/dist/viewer/render.d.ts +19 -0
- package/dist/viewer/render.js +162 -0
- package/dist/viewer/render.js.map +1 -0
- package/dist/viewer/tui.d.ts +11 -0
- package/dist/viewer/tui.js +140 -0
- package/dist/viewer/tui.js.map +1 -0
- package/dist/viewer/watch.d.ts +9 -0
- package/dist/viewer/watch.js +46 -0
- package/dist/viewer/watch.js.map +1 -0
- package/dist/workflows/decision.d.ts +25 -0
- package/dist/workflows/decision.js +96 -0
- package/dist/workflows/decision.js.map +1 -0
- package/dist/workflows/definition.d.ts +9 -0
- package/dist/workflows/definition.js +61 -0
- package/dist/workflows/definition.js.map +1 -0
- package/dist/workflows/engine.d.ts +65 -0
- package/dist/workflows/engine.js +574 -0
- package/dist/workflows/engine.js.map +1 -0
- package/dist/workflows/errors.d.ts +9 -0
- package/dist/workflows/errors.js +24 -0
- package/dist/workflows/errors.js.map +1 -0
- package/dist/workflows/graph.d.ts +17 -0
- package/dist/workflows/graph.js +127 -0
- package/dist/workflows/graph.js.map +1 -0
- package/dist/workflows/index.d.ts +11 -0
- package/dist/workflows/index.js +11 -0
- package/dist/workflows/index.js.map +1 -0
- package/dist/workflows/json.d.ts +14 -0
- package/dist/workflows/json.js +134 -0
- package/dist/workflows/json.js.map +1 -0
- package/dist/workflows/loader.d.ts +28 -0
- package/dist/workflows/loader.js +94 -0
- package/dist/workflows/loader.js.map +1 -0
- package/dist/workflows/schema.d.ts +7 -0
- package/dist/workflows/schema.js +176 -0
- package/dist/workflows/schema.js.map +1 -0
- package/dist/workflows/shell.d.ts +9 -0
- package/dist/workflows/shell.js +177 -0
- package/dist/workflows/shell.js.map +1 -0
- package/dist/workflows/store.d.ts +35 -0
- package/dist/workflows/store.js +181 -0
- package/dist/workflows/store.js.map +1 -0
- package/dist/workflows/text.d.ts +10 -0
- package/dist/workflows/text.js +32 -0
- package/dist/workflows/text.js.map +1 -0
- package/dist/workflows/types.d.ts +280 -0
- package/dist/workflows/types.js +2 -0
- package/dist/workflows/types.js.map +1 -0
- package/docs/development.md +130 -0
- package/docs/run-bundles.md +114 -0
- package/docs/workflows.md +311 -0
- package/examples/workflows/autoimplement.workflow.ts +92 -0
- package/examples/workflows/autoresearch.workflow.ts +139 -0
- package/examples/workflows/branch.workflow.ts +63 -0
- package/examples/workflows/echo.workflow.ts +23 -0
- package/examples/workflows/elegant-solution.workflow.ts +95 -0
- package/examples/workflows/shell.workflow.ts +31 -0
- package/examples/workflows/two-turn.workflow.ts +64 -0
- package/package.json +80 -0
- package/src/extension/executor.ts +251 -0
- package/src/extension/index.ts +627 -0
- package/src/extension/widget.ts +183 -0
- package/src/render/ansi.ts +47 -0
- package/src/render/canvas.ts +196 -0
- package/src/render/format.ts +19 -0
- package/src/render/graph-render.ts +738 -0
- package/src/render/graph.ts +341 -0
- package/src/viewer/cli.ts +150 -0
- package/src/viewer/render.ts +236 -0
- package/src/viewer/tui.ts +159 -0
- package/src/viewer/watch.ts +55 -0
- package/src/workflows/decision.ts +127 -0
- package/src/workflows/definition.ts +104 -0
- package/src/workflows/engine.ts +793 -0
- package/src/workflows/errors.ts +27 -0
- package/src/workflows/graph.ts +161 -0
- package/src/workflows/index.ts +76 -0
- package/src/workflows/json.ts +155 -0
- package/src/workflows/loader.ts +123 -0
- package/src/workflows/schema.ts +218 -0
- package/src/workflows/shell.ts +199 -0
- package/src/workflows/store.ts +234 -0
- package/src/workflows/text.ts +34 -0
- package/src/workflows/types.ts +318 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { ansi, stripAnsi } from "../render/ansi.js";
|
|
2
|
+
import { renderGraphLines } from "../render/graph-render.js";
|
|
3
|
+
import { sanitizeText } from "../workflows/text.js";
|
|
4
|
+
const STATUS_GLYPHS = {
|
|
5
|
+
running: "◐",
|
|
6
|
+
waiting: "⏸",
|
|
7
|
+
completed: "✓",
|
|
8
|
+
failed: "✗",
|
|
9
|
+
timed_out: "✗",
|
|
10
|
+
cancelled: "✗",
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* pi renders at most this many widget lines (InteractiveMode.MAX_WIDGET_LINES)
|
|
14
|
+
* and appends its own "(widget truncated)" marker beyond it. Stay inside the
|
|
15
|
+
* budget and choose which graph rows to show instead of losing the bottom.
|
|
16
|
+
*/
|
|
17
|
+
const PI_MAX_WIDGET_LINES = 10;
|
|
18
|
+
export function nodeGlyph(state, nodeId) {
|
|
19
|
+
if (state.currentNode === nodeId) {
|
|
20
|
+
return "◐";
|
|
21
|
+
}
|
|
22
|
+
const result = state.results[nodeId];
|
|
23
|
+
if (!result) {
|
|
24
|
+
return "·";
|
|
25
|
+
}
|
|
26
|
+
if (state.waitingOn === nodeId) {
|
|
27
|
+
return "⏸";
|
|
28
|
+
}
|
|
29
|
+
return result.outcome === "ok" ? "✓" : "✗";
|
|
30
|
+
}
|
|
31
|
+
/** Node ids in a stable display order: definition order from the snapshot. */
|
|
32
|
+
export function displayNodeIds(snapshot) {
|
|
33
|
+
return Object.keys(snapshot.nodes);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Live-progress view for the in-pi widget: a header plus the same boxed
|
|
37
|
+
* graph the standalone viewer draws. When the graph is taller than pi's
|
|
38
|
+
* widget budget, a window is shown with ↑/↓ overflow markers — centered on
|
|
39
|
+
* the active node by default, or at `scroll` when the user scrolled
|
|
40
|
+
* manually. Pure so it can be tested without a TUI.
|
|
41
|
+
*/
|
|
42
|
+
export function buildWidgetView(state, snapshot, now = new Date(), scroll = null, held = false) {
|
|
43
|
+
// `held` covers pauses the state cannot see yet: an escape-interrupted
|
|
44
|
+
// step or a pause requested while the current node is still finishing.
|
|
45
|
+
const paused = held || state.paused === true;
|
|
46
|
+
const glyph = paused ? "⏸" : STATUS_GLYPHS[state.status];
|
|
47
|
+
const statusText = paused ? "paused" : state.status;
|
|
48
|
+
// Titles, status details, and errors can carry model- or shell-controlled
|
|
49
|
+
// text; never let escape sequences or newlines reach the terminal.
|
|
50
|
+
const title = state.runTitle ? ` — ${sanitizeText(state.runTitle)}` : "";
|
|
51
|
+
const header = `${glyph} workflow ${sanitizeText(state.workflowName)}${title} [${statusText}]`;
|
|
52
|
+
const footer = [];
|
|
53
|
+
if (state.error) {
|
|
54
|
+
footer.push(` error: ${truncate(sanitizeText(state.error), 120)}`);
|
|
55
|
+
}
|
|
56
|
+
if (state.status === "waiting" && state.waitingOn) {
|
|
57
|
+
footer.push(` waiting on checkpoint: ${state.waitingOn}`);
|
|
58
|
+
}
|
|
59
|
+
const budget = PI_MAX_WIDGET_LINES - 1 - footer.length;
|
|
60
|
+
const graph = renderGraphLines({ state, snapshot }, state.steps.length - 1, now, {
|
|
61
|
+
nodeStyle: "box",
|
|
62
|
+
});
|
|
63
|
+
if (graph.length === 0) {
|
|
64
|
+
return {
|
|
65
|
+
lines: [header, ` ${compactNodeStrip(state, snapshot)}`, ...footer],
|
|
66
|
+
scroll: 0,
|
|
67
|
+
maxScroll: 0,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
const windowed = windowLines(graph, budget, scroll ?? focusLine(graph, state), scroll !== null);
|
|
71
|
+
return {
|
|
72
|
+
lines: [header, ...windowed.lines.map((line) => ` ${line}`), ...footer],
|
|
73
|
+
scroll: windowed.scroll,
|
|
74
|
+
maxScroll: windowed.maxScroll,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
/** Back-compatible line view following the active node. */
|
|
78
|
+
export function buildWidgetLines(state, snapshot, now = new Date()) {
|
|
79
|
+
return buildWidgetView(state, snapshot, now).lines;
|
|
80
|
+
}
|
|
81
|
+
/** The graph row the window should center on: the active or waiting node. */
|
|
82
|
+
function focusLine(graph, state) {
|
|
83
|
+
const active = graph.findIndex((line) => stripAnsi(line).includes("◐"));
|
|
84
|
+
if (active !== -1) {
|
|
85
|
+
return active;
|
|
86
|
+
}
|
|
87
|
+
if (state.waitingOn) {
|
|
88
|
+
const waiting = graph.findIndex((line) => stripAnsi(line).includes(state.waitingOn));
|
|
89
|
+
if (waiting !== -1) {
|
|
90
|
+
return waiting;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return graph.length - 1;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Slice `lines` to at most `budget` rows, marking hidden rows at either end.
|
|
97
|
+
* Markers count against the budget. `anchor` is a row to center on (follow
|
|
98
|
+
* mode) or the requested first visible row (manual scroll).
|
|
99
|
+
*/
|
|
100
|
+
function windowLines(lines, budget, anchor, anchorIsStart) {
|
|
101
|
+
if (lines.length <= budget) {
|
|
102
|
+
return { lines, scroll: 0, maxScroll: 0 };
|
|
103
|
+
}
|
|
104
|
+
// First pass decides which markers exist; the second re-fits within the
|
|
105
|
+
// remaining space. A marker flag can only turn on (never off) in the
|
|
106
|
+
// second pass, so the result stays within budget.
|
|
107
|
+
let inner = budget;
|
|
108
|
+
for (let pass = 0; pass < 2; pass += 1) {
|
|
109
|
+
const start = clampStart(anchor, inner, lines.length, anchorIsStart);
|
|
110
|
+
inner = budget - (start > 0 ? 1 : 0) - (start + inner < lines.length ? 1 : 0);
|
|
111
|
+
}
|
|
112
|
+
const start = clampStart(anchor, inner, lines.length, anchorIsStart);
|
|
113
|
+
const end = start + inner;
|
|
114
|
+
const out = [];
|
|
115
|
+
if (start > 0) {
|
|
116
|
+
out.push(ansi.dim(`↑ ${start} more`));
|
|
117
|
+
}
|
|
118
|
+
out.push(...lines.slice(start, end));
|
|
119
|
+
if (end < lines.length) {
|
|
120
|
+
out.push(ansi.dim(`↓ ${lines.length - end} more · shift+↑/↓ scroll`));
|
|
121
|
+
}
|
|
122
|
+
return { lines: out, scroll: start, maxScroll: Math.max(0, lines.length - inner) };
|
|
123
|
+
}
|
|
124
|
+
function clampStart(anchor, inner, total, anchorIsStart) {
|
|
125
|
+
const start = anchorIsStart ? anchor : anchor - Math.floor(inner / 2);
|
|
126
|
+
return Math.max(0, Math.min(start, total - inner));
|
|
127
|
+
}
|
|
128
|
+
function compactNodeStrip(state, snapshot) {
|
|
129
|
+
return displayNodeIds(snapshot)
|
|
130
|
+
.map((nodeId) => {
|
|
131
|
+
const marker = nodeGlyph(state, nodeId);
|
|
132
|
+
const detail = state.currentNode === nodeId && state.statusDetail
|
|
133
|
+
? ` (${sanitizeText(state.statusDetail)})`
|
|
134
|
+
: "";
|
|
135
|
+
return `${marker} ${nodeId}${detail}`;
|
|
136
|
+
})
|
|
137
|
+
.join(" ");
|
|
138
|
+
}
|
|
139
|
+
function truncate(text, maxLength) {
|
|
140
|
+
return text.length <= maxLength ? text : `${text.slice(0, maxLength - 1)}…`;
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=widget.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"widget.js","sourceRoot":"","sources":["../../src/extension/widget.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { sanitizeText, stripAnsi } from "../workflows/text.js";
|
|
2
|
+
export { sanitizeText, stripAnsi };
|
|
3
|
+
export declare const ansi: {
|
|
4
|
+
bold: (text: string) => string;
|
|
5
|
+
dim: (text: string) => string;
|
|
6
|
+
red: (text: string) => string;
|
|
7
|
+
green: (text: string) => string;
|
|
8
|
+
yellow: (text: string) => string;
|
|
9
|
+
blue: (text: string) => string;
|
|
10
|
+
magenta: (text: string) => string;
|
|
11
|
+
cyan: (text: string) => string;
|
|
12
|
+
};
|
|
13
|
+
/** Visible length of a string, ignoring ANSI escape sequences. */
|
|
14
|
+
export declare function visibleLength(text: string): number;
|
|
15
|
+
/** Truncate to a visible width, keeping ANSI sequences intact by stripping them first when needed. */
|
|
16
|
+
export declare function fitWidth(text: string, width: number): string;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { sanitizeText, stripAnsi } from "../workflows/text.js";
|
|
2
|
+
export { sanitizeText, stripAnsi };
|
|
3
|
+
/**
|
|
4
|
+
* Colors are on for interactive terminals only, so piped output stays clean.
|
|
5
|
+
* `NO_COLOR`/`PI_WORKFLOWS_NO_COLOR` force them off; `FORCE_COLOR` forces
|
|
6
|
+
* them on. Evaluated per call so env changes (tests) take effect.
|
|
7
|
+
*/
|
|
8
|
+
function colorEnabled() {
|
|
9
|
+
if (process.env.NO_COLOR !== undefined || process.env.PI_WORKFLOWS_NO_COLOR !== undefined) {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
if (process.env.FORCE_COLOR !== undefined) {
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
return process.stdout.isTTY === true;
|
|
16
|
+
}
|
|
17
|
+
function wrap(code, text) {
|
|
18
|
+
return colorEnabled() ? `\u001b[${code}m${text}\u001b[0m` : text;
|
|
19
|
+
}
|
|
20
|
+
export const ansi = {
|
|
21
|
+
bold: (text) => wrap("1", text),
|
|
22
|
+
dim: (text) => wrap("2", text),
|
|
23
|
+
red: (text) => wrap("31", text),
|
|
24
|
+
green: (text) => wrap("32", text),
|
|
25
|
+
yellow: (text) => wrap("33", text),
|
|
26
|
+
blue: (text) => wrap("34", text),
|
|
27
|
+
magenta: (text) => wrap("35", text),
|
|
28
|
+
cyan: (text) => wrap("36", text),
|
|
29
|
+
};
|
|
30
|
+
/** Visible length of a string, ignoring ANSI escape sequences. */
|
|
31
|
+
export function visibleLength(text) {
|
|
32
|
+
return stripAnsi(text).length;
|
|
33
|
+
}
|
|
34
|
+
/** Truncate to a visible width, keeping ANSI sequences intact by stripping them first when needed. */
|
|
35
|
+
export function fitWidth(text, width) {
|
|
36
|
+
if (visibleLength(text) <= width) {
|
|
37
|
+
return text;
|
|
38
|
+
}
|
|
39
|
+
const plain = stripAnsi(text);
|
|
40
|
+
return width <= 1 ? plain.slice(0, width) : `${plain.slice(0, width - 1)}…`;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=ansi.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ansi.js","sourceRoot":"","sources":["../../src/render/ansi.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAE/D,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,CAAC;AAEnC;;;;GAIG;AACH,SAAS,YAAY;IACnB,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,SAAS,IAAI,OAAO,CAAC,GAAG,CAAC,qBAAqB,KAAK,SAAS,EAAE,CAAC;QAC1F,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,OAAO,CAAC,GAAG,CAAC,WAAW,KAAK,SAAS,EAAE,CAAC;QAC1C,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,OAAO,CAAC,MAAM,CAAC,KAAK,KAAK,IAAI,CAAC;AACvC,CAAC;AAED,SAAS,IAAI,CAAC,IAAY,EAAE,IAAY;IACtC,OAAO,YAAY,EAAE,CAAC,CAAC,CAAC,UAAU,IAAI,IAAI,IAAI,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC;AACnE,CAAC;AAED,MAAM,CAAC,MAAM,IAAI,GAAG;IAClB,IAAI,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC;IACvC,GAAG,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC;IACtC,GAAG,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC;IACvC,KAAK,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC;IACzC,MAAM,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC;IAC1C,IAAI,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC;IACxC,OAAO,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC;IAC3C,IAAI,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC;CACzC,CAAC;AAEF,kEAAkE;AAClE,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC;AAChC,CAAC;AAED,sGAAsG;AACtG,MAAM,UAAU,QAAQ,CAAC,IAAY,EAAE,KAAa;IAClD,IAAI,aAAa,CAAC,IAAI,CAAC,IAAI,KAAK,EAAE,CAAC;QACjC,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,KAAK,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IAC9B,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,GAAG,CAAC;AAC9E,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A sparse character grid for drawing the workflow graph. Box-drawing
|
|
3
|
+
* characters merge by connectivity (│ crossing ─ becomes ┼) so overlapping
|
|
4
|
+
* edge polylines join instead of overwriting each other.
|
|
5
|
+
*/
|
|
6
|
+
export type CanvasStyle = "plain" | "dim" | "taken" | "active" | "back" | "ok" | "fail" | "warn";
|
|
7
|
+
export declare const UP = 1;
|
|
8
|
+
export declare const DOWN = 2;
|
|
9
|
+
export declare const LEFT = 4;
|
|
10
|
+
export declare const RIGHT = 8;
|
|
11
|
+
/** Which sides each box-drawing character connects to (exported for tests). */
|
|
12
|
+
export declare const CHAR_TO_MASK: Record<string, number>;
|
|
13
|
+
export declare class CharCanvas {
|
|
14
|
+
private readonly cells;
|
|
15
|
+
private maxX;
|
|
16
|
+
private maxY;
|
|
17
|
+
private row;
|
|
18
|
+
/** Place a single character, merging box-drawing connectivity. */
|
|
19
|
+
put(x: number, y: number, char: string, style?: CanvasStyle): void;
|
|
20
|
+
/** Write a text run left to right (labels, node lines). */
|
|
21
|
+
text(x: number, y: number, value: string, style?: CanvasStyle): void;
|
|
22
|
+
/**
|
|
23
|
+
* Write text only when every target cell is empty. Returns whether the
|
|
24
|
+
* text was written. Used for optional decorations (edge labels) that must
|
|
25
|
+
* never corrupt lines or nodes already on the canvas.
|
|
26
|
+
*/
|
|
27
|
+
textIfEmpty(x: number, y: number, value: string, style?: CanvasStyle): boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Write text over a plain horizontal run, replacing `─` cells only.
|
|
30
|
+
* Refuses (returns false) unless every target cell and both flanking
|
|
31
|
+
* cells are exactly `─`, so a label can never overwrite or sit against
|
|
32
|
+
* corners, crossings, or other labels. Spaces in `value` become real
|
|
33
|
+
* blanks, visually breaking the run around the label on purpose.
|
|
34
|
+
*/
|
|
35
|
+
textOverRun(x: number, y: number, value: string, style?: CanvasStyle): boolean;
|
|
36
|
+
hline(y: number, x1: number, x2: number, style?: CanvasStyle): void;
|
|
37
|
+
vline(x: number, y1: number, y2: number, style?: CanvasStyle): void;
|
|
38
|
+
/** Render to lines; `paint` applies terminal styling per character run. */
|
|
39
|
+
render(paint: (text: string, style: CanvasStyle) => string): string[];
|
|
40
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A sparse character grid for drawing the workflow graph. Box-drawing
|
|
3
|
+
* characters merge by connectivity (│ crossing ─ becomes ┼) so overlapping
|
|
4
|
+
* edge polylines join instead of overwriting each other.
|
|
5
|
+
*/
|
|
6
|
+
export const UP = 1;
|
|
7
|
+
export const DOWN = 2;
|
|
8
|
+
export const LEFT = 4;
|
|
9
|
+
export const RIGHT = 8;
|
|
10
|
+
/** Which sides each box-drawing character connects to (exported for tests). */
|
|
11
|
+
export const CHAR_TO_MASK = {
|
|
12
|
+
"─": LEFT | RIGHT,
|
|
13
|
+
"│": UP | DOWN,
|
|
14
|
+
"┌": DOWN | RIGHT,
|
|
15
|
+
"┐": DOWN | LEFT,
|
|
16
|
+
"└": UP | RIGHT,
|
|
17
|
+
"┘": UP | LEFT,
|
|
18
|
+
"├": UP | DOWN | RIGHT,
|
|
19
|
+
"┤": UP | DOWN | LEFT,
|
|
20
|
+
"┬": DOWN | LEFT | RIGHT,
|
|
21
|
+
"┴": UP | LEFT | RIGHT,
|
|
22
|
+
"┼": UP | DOWN | LEFT | RIGHT,
|
|
23
|
+
};
|
|
24
|
+
const MASK_TO_CHAR = new Map(Object.entries(CHAR_TO_MASK).map(([char, mask]) => [mask, char]));
|
|
25
|
+
/** Styles later in this list win when merged lines overlap. */
|
|
26
|
+
const STYLE_PRIORITY = [
|
|
27
|
+
"plain",
|
|
28
|
+
"dim",
|
|
29
|
+
"back",
|
|
30
|
+
"warn",
|
|
31
|
+
"ok",
|
|
32
|
+
"fail",
|
|
33
|
+
"taken",
|
|
34
|
+
"active",
|
|
35
|
+
];
|
|
36
|
+
function mergeStyles(a, b) {
|
|
37
|
+
return STYLE_PRIORITY.indexOf(a) >= STYLE_PRIORITY.indexOf(b) ? a : b;
|
|
38
|
+
}
|
|
39
|
+
export class CharCanvas {
|
|
40
|
+
cells = new Map();
|
|
41
|
+
maxX = 0;
|
|
42
|
+
maxY = 0;
|
|
43
|
+
row(y) {
|
|
44
|
+
let row = this.cells.get(y);
|
|
45
|
+
if (!row) {
|
|
46
|
+
row = new Map();
|
|
47
|
+
this.cells.set(y, row);
|
|
48
|
+
}
|
|
49
|
+
this.maxY = Math.max(this.maxY, y);
|
|
50
|
+
return row;
|
|
51
|
+
}
|
|
52
|
+
/** Place a single character, merging box-drawing connectivity. */
|
|
53
|
+
put(x, y, char, style = "plain") {
|
|
54
|
+
if (x < 0 || y < 0) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const row = this.row(y);
|
|
58
|
+
this.maxX = Math.max(this.maxX, x);
|
|
59
|
+
const existing = row.get(x);
|
|
60
|
+
// Spaces never occupy cells; textOverRun handles deliberate padding.
|
|
61
|
+
if (char === " ") {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
if (existing) {
|
|
65
|
+
const existingMask = CHAR_TO_MASK[existing.char];
|
|
66
|
+
const incomingMask = CHAR_TO_MASK[char];
|
|
67
|
+
if (existingMask !== undefined && incomingMask !== undefined) {
|
|
68
|
+
row.set(x, {
|
|
69
|
+
char: MASK_TO_CHAR.get(existingMask | incomingMask) ?? char,
|
|
70
|
+
style: mergeStyles(existing.style, style),
|
|
71
|
+
});
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
// Non-line characters (labels, glyphs, arrows) win over lines; between
|
|
75
|
+
// two non-line characters the newest wins.
|
|
76
|
+
if (existingMask === undefined && incomingMask !== undefined) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
row.set(x, { char, style });
|
|
81
|
+
}
|
|
82
|
+
/** Write a text run left to right (labels, node lines). */
|
|
83
|
+
text(x, y, value, style = "plain") {
|
|
84
|
+
for (const [index, char] of [...value].entries()) {
|
|
85
|
+
this.put(x + index, y, char, style);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Write text only when every target cell is empty. Returns whether the
|
|
90
|
+
* text was written. Used for optional decorations (edge labels) that must
|
|
91
|
+
* never corrupt lines or nodes already on the canvas.
|
|
92
|
+
*/
|
|
93
|
+
textIfEmpty(x, y, value, style = "plain") {
|
|
94
|
+
if (x < 0 || y < 0) {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
const row = this.cells.get(y);
|
|
98
|
+
const width = [...value].length;
|
|
99
|
+
for (let index = 0; index < width; index += 1) {
|
|
100
|
+
if (row?.has(x + index)) {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
this.text(x, y, value, style);
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Write text over a plain horizontal run, replacing `─` cells only.
|
|
109
|
+
* Refuses (returns false) unless every target cell and both flanking
|
|
110
|
+
* cells are exactly `─`, so a label can never overwrite or sit against
|
|
111
|
+
* corners, crossings, or other labels. Spaces in `value` become real
|
|
112
|
+
* blanks, visually breaking the run around the label on purpose.
|
|
113
|
+
*/
|
|
114
|
+
textOverRun(x, y, value, style = "plain") {
|
|
115
|
+
if (x < 1 || y < 0) {
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
const chars = [...value];
|
|
119
|
+
const row = this.cells.get(y);
|
|
120
|
+
for (let index = -1; index <= chars.length; index += 1) {
|
|
121
|
+
if (row?.get(x + index)?.char !== "─") {
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
for (const [index, char] of chars.entries()) {
|
|
126
|
+
this.row(y).set(x + index, { char, style });
|
|
127
|
+
}
|
|
128
|
+
this.maxX = Math.max(this.maxX, x + chars.length - 1);
|
|
129
|
+
return true;
|
|
130
|
+
}
|
|
131
|
+
hline(y, x1, x2, style = "plain") {
|
|
132
|
+
const [start, end] = x1 <= x2 ? [x1, x2] : [x2, x1];
|
|
133
|
+
for (let x = start; x <= end; x += 1) {
|
|
134
|
+
this.put(x, y, "─", style);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
vline(x, y1, y2, style = "plain") {
|
|
138
|
+
const [start, end] = y1 <= y2 ? [y1, y2] : [y2, y1];
|
|
139
|
+
for (let y = start; y <= end; y += 1) {
|
|
140
|
+
this.put(x, y, "│", style);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
/** Render to lines; `paint` applies terminal styling per character run. */
|
|
144
|
+
render(paint) {
|
|
145
|
+
const lines = [];
|
|
146
|
+
for (let y = 0; y <= this.maxY; y += 1) {
|
|
147
|
+
const row = this.cells.get(y);
|
|
148
|
+
if (!row || row.size === 0) {
|
|
149
|
+
lines.push("");
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
let line = "";
|
|
153
|
+
let runText = "";
|
|
154
|
+
let runStyle = "plain";
|
|
155
|
+
const flush = () => {
|
|
156
|
+
if (runText.length > 0) {
|
|
157
|
+
line += paint(runText, runStyle);
|
|
158
|
+
runText = "";
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
for (let x = 0; x <= this.maxX; x += 1) {
|
|
162
|
+
const cell = row.get(x);
|
|
163
|
+
const char = cell?.char ?? " ";
|
|
164
|
+
const style = cell?.style ?? "plain";
|
|
165
|
+
if (style !== runStyle) {
|
|
166
|
+
flush();
|
|
167
|
+
runStyle = style;
|
|
168
|
+
}
|
|
169
|
+
runText += char;
|
|
170
|
+
}
|
|
171
|
+
flush();
|
|
172
|
+
lines.push(line.replace(/\s+$/, ""));
|
|
173
|
+
}
|
|
174
|
+
return lines;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
//# sourceMappingURL=canvas.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canvas.js","sourceRoot":"","sources":["../../src/render/canvas.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AACpB,MAAM,CAAC,MAAM,IAAI,GAAG,CAAC,CAAC;AACtB,MAAM,CAAC,MAAM,IAAI,GAAG,CAAC,CAAC;AACtB,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,CAAC;AAEvB,+EAA+E;AAC/E,MAAM,CAAC,MAAM,YAAY,GAA2B;IAClD,GAAG,EAAE,IAAI,GAAG,KAAK;IACjB,GAAG,EAAE,EAAE,GAAG,IAAI;IACd,GAAG,EAAE,IAAI,GAAG,KAAK;IACjB,GAAG,EAAE,IAAI,GAAG,IAAI;IAChB,GAAG,EAAE,EAAE,GAAG,KAAK;IACf,GAAG,EAAE,EAAE,GAAG,IAAI;IACd,GAAG,EAAE,EAAE,GAAG,IAAI,GAAG,KAAK;IACtB,GAAG,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI;IACrB,GAAG,EAAE,IAAI,GAAG,IAAI,GAAG,KAAK;IACxB,GAAG,EAAE,EAAE,GAAG,IAAI,GAAG,KAAK;IACtB,GAAG,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI,GAAG,KAAK;CAC9B,CAAC;AAEF,MAAM,YAAY,GAAG,IAAI,GAAG,CAC1B,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CACjE,CAAC;AAEF,+DAA+D;AAC/D,MAAM,cAAc,GAAkB;IACpC,OAAO;IACP,KAAK;IACL,MAAM;IACN,MAAM;IACN,IAAI;IACJ,MAAM;IACN,OAAO;IACP,QAAQ;CACT,CAAC;AAEF,SAAS,WAAW,CAAC,CAAc,EAAE,CAAc;IACjD,OAAO,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACxE,CAAC;AAED,MAAM,OAAO,UAAU;IACJ,KAAK,GAAG,IAAI,GAAG,EAAmC,CAAC;IAC5D,IAAI,GAAG,CAAC,CAAC;IACT,IAAI,GAAG,CAAC,CAAC;IAET,GAAG,CAAC,CAAS;QACnB,IAAI,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAC5B,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,GAAG,GAAG,IAAI,GAAG,EAAE,CAAC;YAChB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;QACzB,CAAC;QACD,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QACnC,OAAO,GAAG,CAAC;IACb,CAAC;IAED,kEAAkE;IAClE,GAAG,CAAC,CAAS,EAAE,CAAS,EAAE,IAAY,EAAE,KAAK,GAAgB,OAAO;QAClE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YACnB,OAAO;QACT,CAAC;QACD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QACxB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QACnC,MAAM,QAAQ,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAC5B,qEAAqE;QACrE,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;YACjB,OAAO;QACT,CAAC;QACD,IAAI,QAAQ,EAAE,CAAC;YACb,MAAM,YAAY,GAAG,YAAY,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;YACjD,MAAM,YAAY,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;YACxC,IAAI,YAAY,KAAK,SAAS,IAAI,YAAY,KAAK,SAAS,EAAE,CAAC;gBAC7D,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE;oBACT,IAAI,EAAE,YAAY,CAAC,GAAG,CAAC,YAAY,GAAG,YAAY,CAAC,IAAI,IAAI;oBAC3D,KAAK,EAAE,WAAW,CAAC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;iBAC1C,CAAC,CAAC;gBACH,OAAO;YACT,CAAC;YACD,uEAAuE;YACvE,2CAA2C;YAC3C,IAAI,YAAY,KAAK,SAAS,IAAI,YAAY,KAAK,SAAS,EAAE,CAAC;gBAC7D,OAAO;YACT,CAAC;QACH,CAAC;QACD,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC9B,CAAC;IAED,2DAA2D;IAC3D,IAAI,CAAC,CAAS,EAAE,CAAS,EAAE,KAAa,EAAE,KAAK,GAAgB,OAAO;QACpE,KAAK,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC;YACjD,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;QACtC,CAAC;IACH,CAAC;IAED;;;;OAIG;IACH,WAAW,CAAC,CAAS,EAAE,CAAS,EAAE,KAAa,EAAE,KAAK,GAAgB,OAAO;QAC3E,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YACnB,OAAO,KAAK,CAAC;QACf,CAAC;QACD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAC9B,MAAM,KAAK,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC;QAChC,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,KAAK,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;YAC9C,IAAI,GAAG,EAAE,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC,EAAE,CAAC;gBACxB,OAAO,KAAK,CAAC;YACf,CAAC;QACH,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;QAC9B,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;;;OAMG;IACH,WAAW,CAAC,CAAS,EAAE,CAAS,EAAE,KAAa,EAAE,KAAK,GAAgB,OAAO;QAC3E,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YACnB,OAAO,KAAK,CAAC;QACf,CAAC;QACD,MAAM,KAAK,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC;QACzB,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAC9B,KAAK,IAAI,KAAK,GAAG,CAAC,CAAC,EAAE,KAAK,IAAI,KAAK,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;YACvD,IAAI,GAAG,EAAE,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC,EAAE,IAAI,KAAK,GAAG,EAAE,CAAC;gBACtC,OAAO,KAAK,CAAC;YACf,CAAC;QACH,CAAC;QACD,KAAK,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC;YAC5C,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,KAAK,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QAC9C,CAAC;QACD,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QACtD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,KAAK,CAAC,CAAS,EAAE,EAAU,EAAE,EAAU,EAAE,KAAK,GAAgB,OAAO;QACnE,MAAM,CAAC,KAAK,EAAE,GAAG,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;QACpD,KAAK,IAAI,CAAC,GAAG,KAAK,EAAE,CAAC,IAAI,GAAG,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YACrC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;QAC7B,CAAC;IACH,CAAC;IAED,KAAK,CAAC,CAAS,EAAE,EAAU,EAAE,EAAU,EAAE,KAAK,GAAgB,OAAO;QACnE,MAAM,CAAC,KAAK,EAAE,GAAG,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;QACpD,KAAK,IAAI,CAAC,GAAG,KAAK,EAAE,CAAC,IAAI,GAAG,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YACrC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;QAC7B,CAAC;IACH,CAAC;IAED,2EAA2E;IAC3E,MAAM,CAAC,KAAmD;QACxD,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YACvC,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YAC9B,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;gBAC3B,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;gBACf,SAAS;YACX,CAAC;YACD,IAAI,IAAI,GAAG,EAAE,CAAC;YACd,IAAI,OAAO,GAAG,EAAE,CAAC;YACjB,IAAI,QAAQ,GAAgB,OAAO,CAAC;YACpC,MAAM,KAAK,GAAG,GAAG,EAAE;gBACjB,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBACvB,IAAI,IAAI,KAAK,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;oBACjC,OAAO,GAAG,EAAE,CAAC;gBACf,CAAC;YACH,CAAC,CAAC;YACF,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;gBACvC,MAAM,IAAI,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;gBACxB,MAAM,IAAI,GAAG,IAAI,EAAE,IAAI,IAAI,GAAG,CAAC;gBAC/B,MAAM,KAAK,GAAG,IAAI,EAAE,KAAK,IAAI,OAAO,CAAC;gBACrC,IAAI,KAAK,KAAK,QAAQ,EAAE,CAAC;oBACvB,KAAK,EAAE,CAAC;oBACR,QAAQ,GAAG,KAAK,CAAC;gBACnB,CAAC;gBACD,OAAO,IAAI,IAAI,CAAC;YAClB,CAAC;YACD,KAAK,EAAE,CAAC;YACR,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC;QACvC,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;CACF"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export function formatDuration(durationMs) {
|
|
2
|
+
if (durationMs < 1_000) {
|
|
3
|
+
return `${Math.max(durationMs, 0)}ms`;
|
|
4
|
+
}
|
|
5
|
+
const seconds = durationMs / 1_000;
|
|
6
|
+
if (seconds < 60) {
|
|
7
|
+
return `${seconds.toFixed(seconds < 10 ? 1 : 0)}s`;
|
|
8
|
+
}
|
|
9
|
+
const minutes = Math.floor(seconds / 60);
|
|
10
|
+
const rest = Math.round(seconds % 60);
|
|
11
|
+
return `${minutes}m${rest.toString().padStart(2, "0")}s`;
|
|
12
|
+
}
|
|
13
|
+
export function runElapsedMs(state, now = new Date()) {
|
|
14
|
+
const end = state.finishedAt ? Date.parse(state.finishedAt) : now.getTime();
|
|
15
|
+
return Math.max(0, end - Date.parse(state.startedAt));
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=format.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/render/format.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,cAAc,CAAC,UAAkB;IAC/C,IAAI,UAAU,GAAG,KAAK,EAAE,CAAC;QACvB,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC,IAAI,CAAC;IACxC,CAAC;IACD,MAAM,OAAO,GAAG,UAAU,GAAG,KAAK,CAAC;IACnC,IAAI,OAAO,GAAG,EAAE,EAAE,CAAC;QACjB,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;IACrD,CAAC;IACD,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC,CAAC;IACzC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC,CAAC;IACtC,OAAO,GAAG,OAAO,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC;AAC3D,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,KAAuB,EAAE,GAAG,GAAS,IAAI,IAAI,EAAE;IAC1E,MAAM,GAAG,GAAG,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;IAC5E,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC;AACxD,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { WorkflowDefinitionSnapshot, WorkflowRunState } from "../workflows/types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Renders the workflow DAG as text, mirroring the acpx replay viewer's graph
|
|
4
|
+
* pane: statuses derive from the steps visible up to the selected step, taken
|
|
5
|
+
* transitions highlight, switch branches carry case labels, and loop edges
|
|
6
|
+
* route through a right-hand gutter.
|
|
7
|
+
*/
|
|
8
|
+
/** Everything the graph needs; a LoadedRunBundle satisfies this shape. */
|
|
9
|
+
export type GraphView = {
|
|
10
|
+
state: WorkflowRunState;
|
|
11
|
+
snapshot: WorkflowDefinitionSnapshot | null;
|
|
12
|
+
};
|
|
13
|
+
/** How node cells are drawn: single text lines or bordered boxes. */
|
|
14
|
+
export type GraphNodeStyle = "line" | "box";
|
|
15
|
+
export type GraphRenderOptions = {
|
|
16
|
+
nodeStyle?: GraphNodeStyle;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Render the graph pane. `selectedStepIndex` scrubs the replay position;
|
|
20
|
+
* pass `steps.length - 1` (or larger) for the live view.
|
|
21
|
+
*/
|
|
22
|
+
export declare function renderGraphLines(view: GraphView, selectedStepIndex: number, now?: Date, options?: GraphRenderOptions): string[];
|