@bastani/atomic 0.9.19-alpha.7 → 0.9.19-alpha.9
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/CHANGELOG.md +14 -0
- package/dist/builtin/intercom/CHANGELOG.md +14 -0
- package/dist/builtin/intercom/README.md +7 -7
- package/dist/builtin/intercom/broker/client.ts +4 -0
- package/dist/builtin/intercom/broker/send-handler.ts +18 -1
- package/dist/builtin/intercom/index.bundle.mjs +20 -17
- package/dist/builtin/intercom/package.json +1 -1
- package/dist/builtin/intercom/skills/intercom/SKILL.md +3 -3
- package/dist/builtin/intercom/types.ts +1 -1
- package/dist/builtin/mcp/package.json +1 -1
- package/dist/builtin/subagents/package.json +1 -1
- package/dist/builtin/web-access/CHANGELOG.md +6 -0
- package/dist/builtin/web-access/index.bundle.mjs +19 -17
- package/dist/builtin/web-access/package.json +1 -1
- package/dist/builtin/workflows/CHANGELOG.md +17 -0
- package/dist/builtin/workflows/package.json +1 -1
- package/dist/builtin/workflows/src/extension/index.bundle.mjs +403 -232
- package/docs/intercom/reference.md +4 -0
- package/docs/intercom.md +4 -4
- package/docs/web-access.md +10 -0
- package/docs/workflows/operations.md +8 -3
- package/npm-shrinkwrap.json +32 -32
- package/package.json +3 -3
|
@@ -69012,9 +69012,9 @@ function renderDetailHintRows(detail, width, theme) {
|
|
|
69012
69012
|
}
|
|
69013
69013
|
function formatTime(ms) {
|
|
69014
69014
|
const d = new Date(ms);
|
|
69015
|
-
const hh = String(d.
|
|
69016
|
-
const mm = String(d.
|
|
69017
|
-
const ss = String(d.
|
|
69015
|
+
const hh = String(d.getHours()).padStart(2, "0");
|
|
69016
|
+
const mm = String(d.getMinutes()).padStart(2, "0");
|
|
69017
|
+
const ss = String(d.getSeconds()).padStart(2, "0");
|
|
69018
69018
|
return `${hh}:${mm}:${ss}`;
|
|
69019
69019
|
}
|
|
69020
69020
|
|
|
@@ -72599,7 +72599,7 @@ class GraphCanvas {
|
|
|
72599
72599
|
|
|
72600
72600
|
// dist/builtin/workflows/src/tui/layout.ts
|
|
72601
72601
|
var NODE_W = 24;
|
|
72602
|
-
var NODE_H =
|
|
72602
|
+
var NODE_H = 5;
|
|
72603
72603
|
function computeLayout(stages, opts = {}) {
|
|
72604
72604
|
const colGap = opts.colGap ?? 4;
|
|
72605
72605
|
const rowGap = opts.rowGap ?? 3;
|
|
@@ -72682,17 +72682,255 @@ function computeLayout(stages, opts = {}) {
|
|
|
72682
72682
|
rowIndex.set(ids[i], i);
|
|
72683
72683
|
}
|
|
72684
72684
|
}
|
|
72685
|
+
const heights = new Map(stages.map((stage) => [stage.id, opts.nodeHeight?.(stage) ?? NODE_H]));
|
|
72686
|
+
const verticalOffsets = new Map;
|
|
72687
|
+
const horizontalOffsets = new Map;
|
|
72688
|
+
let depthY = 0;
|
|
72689
|
+
for (const depth of [...colGroups.keys()].sort((a, b) => a - b)) {
|
|
72690
|
+
const ids = colGroups.get(depth);
|
|
72691
|
+
verticalOffsets.set(depth, depthY);
|
|
72692
|
+
let siblingY = 0;
|
|
72693
|
+
let tallest = 0;
|
|
72694
|
+
for (const id of ids) {
|
|
72695
|
+
const height = heights.get(id);
|
|
72696
|
+
horizontalOffsets.set(id, siblingY);
|
|
72697
|
+
siblingY += height + rowGap;
|
|
72698
|
+
tallest = Math.max(tallest, height);
|
|
72699
|
+
}
|
|
72700
|
+
depthY += tallest + rowGap;
|
|
72701
|
+
}
|
|
72685
72702
|
const nodes = [];
|
|
72686
72703
|
for (const s of stages) {
|
|
72687
72704
|
const col = colMap.get(s.id) ?? 0;
|
|
72688
72705
|
const row = rowIndex.get(s.id) ?? 0;
|
|
72689
72706
|
const x = orientation === "vertical" ? row * (NODE_W + colGap) + (centreShift.get(col) ?? 0) : col * (NODE_W + colGap);
|
|
72690
|
-
const y = orientation === "vertical" ? col
|
|
72691
|
-
nodes.push({ stage: s, col, row, x, y });
|
|
72707
|
+
const y = orientation === "vertical" ? verticalOffsets.get(col) : horizontalOffsets.get(s.id);
|
|
72708
|
+
nodes.push({ stage: s, col, row, x, y, ...opts.nodeHeight ? { height: heights.get(s.id) } : {} });
|
|
72692
72709
|
}
|
|
72693
72710
|
return nodes;
|
|
72694
72711
|
}
|
|
72695
72712
|
|
|
72713
|
+
// dist/builtin/workflows/src/tui/node-card.ts
|
|
72714
|
+
import { stripTerminalSequences } from "@earendil-works/pi-tui";
|
|
72715
|
+
function pulseT(phase) {
|
|
72716
|
+
return (Math.sin(phase * Math.PI * 2 - Math.PI / 2) + 1) / 2;
|
|
72717
|
+
}
|
|
72718
|
+
function queuedBadgeCount(count) {
|
|
72719
|
+
if (typeof count !== "number" || !Number.isFinite(count))
|
|
72720
|
+
return 0;
|
|
72721
|
+
return Math.max(0, Math.trunc(count));
|
|
72722
|
+
}
|
|
72723
|
+
function queuedBadgeText(count) {
|
|
72724
|
+
return `✉ ${count} queued`;
|
|
72725
|
+
}
|
|
72726
|
+
function pickBorder(status, focused, phase, theme) {
|
|
72727
|
+
switch (status) {
|
|
72728
|
+
case "running":
|
|
72729
|
+
if (focused)
|
|
72730
|
+
return theme.warning;
|
|
72731
|
+
return lerpColor(theme.borderDim, theme.warning, pulseT(phase));
|
|
72732
|
+
case "paused":
|
|
72733
|
+
return theme.warning;
|
|
72734
|
+
case "awaiting_input":
|
|
72735
|
+
if (focused)
|
|
72736
|
+
return theme.info;
|
|
72737
|
+
return lerpColor(theme.borderDim, theme.info, pulseT(phase));
|
|
72738
|
+
case "completed":
|
|
72739
|
+
return theme.success;
|
|
72740
|
+
case "failed":
|
|
72741
|
+
return theme.error;
|
|
72742
|
+
case "blocked":
|
|
72743
|
+
return theme.dim;
|
|
72744
|
+
case "skipped":
|
|
72745
|
+
return theme.dim;
|
|
72746
|
+
default:
|
|
72747
|
+
return focused ? theme.borderActive : theme.borderDim;
|
|
72748
|
+
}
|
|
72749
|
+
}
|
|
72750
|
+
function durationColor(status, theme) {
|
|
72751
|
+
switch (status) {
|
|
72752
|
+
case "running":
|
|
72753
|
+
return theme.warning;
|
|
72754
|
+
case "paused":
|
|
72755
|
+
return theme.warning;
|
|
72756
|
+
case "awaiting_input":
|
|
72757
|
+
return theme.info;
|
|
72758
|
+
case "completed":
|
|
72759
|
+
return theme.success;
|
|
72760
|
+
case "failed":
|
|
72761
|
+
return theme.error;
|
|
72762
|
+
default:
|
|
72763
|
+
return theme.dim;
|
|
72764
|
+
}
|
|
72765
|
+
}
|
|
72766
|
+
function blockedBadgeText(stage, stages, width) {
|
|
72767
|
+
const base = "↑ blocked";
|
|
72768
|
+
const blockedBy = stage.blockedByStageId;
|
|
72769
|
+
if (!blockedBy)
|
|
72770
|
+
return base;
|
|
72771
|
+
const upstream = stages?.find((s) => s.id === blockedBy)?.name ?? blockedBy;
|
|
72772
|
+
const withUpstream = `${base} by ${upstream}`;
|
|
72773
|
+
if (visibleWidth(withUpstream) <= width)
|
|
72774
|
+
return withUpstream;
|
|
72775
|
+
return base;
|
|
72776
|
+
}
|
|
72777
|
+
function durationText(stage) {
|
|
72778
|
+
const elapsed = elapsedStageMs(stage);
|
|
72779
|
+
return elapsed === undefined ? "—" : fmtDuration(elapsed);
|
|
72780
|
+
}
|
|
72781
|
+
function metaText(stage) {
|
|
72782
|
+
return stage.topologyState === "unavailable" ? "topology unavailable" : "";
|
|
72783
|
+
}
|
|
72784
|
+
function modelText(stage, innerWidth) {
|
|
72785
|
+
const model = stage.model;
|
|
72786
|
+
if (model === undefined || model === "")
|
|
72787
|
+
return "";
|
|
72788
|
+
const slash = model.lastIndexOf("/");
|
|
72789
|
+
const short = slash >= 0 ? model.slice(slash + 1) : model;
|
|
72790
|
+
const level = stage.thinkingLevel;
|
|
72791
|
+
const showLevel = level !== undefined && level !== "" && level !== "off";
|
|
72792
|
+
const suffix = showLevel ? ` · ${level}` : "";
|
|
72793
|
+
const full = `${short}${suffix}`;
|
|
72794
|
+
if (visibleWidth(full) <= innerWidth)
|
|
72795
|
+
return full;
|
|
72796
|
+
const modelSuffix = short.endsWith("-fast") ? "-fast" : "";
|
|
72797
|
+
const name = modelSuffix ? short.slice(0, -modelSuffix.length) : short;
|
|
72798
|
+
const room = Math.max(1, innerWidth - visibleWidth(modelSuffix + suffix));
|
|
72799
|
+
return `${truncateToWidth2(name, room, "…")}${modelSuffix}${suffix}`;
|
|
72800
|
+
}
|
|
72801
|
+
function workflowChildRunRows(stage, width) {
|
|
72802
|
+
const child = stage.workflowChild ?? stage.workflowChildRun;
|
|
72803
|
+
if (child === undefined)
|
|
72804
|
+
return [];
|
|
72805
|
+
return wrapIdentifierLines(child.runId, Math.max(1, width), "run ", "").map((row) => `${row.prefix}${row.chunk}`);
|
|
72806
|
+
}
|
|
72807
|
+
function workflowChildMetaText(stage) {
|
|
72808
|
+
const completed = stage.workflowChild;
|
|
72809
|
+
if (completed !== undefined) {
|
|
72810
|
+
const outputCount = completed.outputCount ?? Object.keys(completed.outputs).length;
|
|
72811
|
+
return outputCount === 1 ? "1 out" : `${outputCount} outs`;
|
|
72812
|
+
}
|
|
72813
|
+
if (stage.workflowChildRun !== undefined)
|
|
72814
|
+
return "live";
|
|
72815
|
+
return;
|
|
72816
|
+
}
|
|
72817
|
+
function joinCompactStatusMeta(status, meta, width) {
|
|
72818
|
+
const candidates = [`${status} · ${meta}`, `${status} ·${meta}`, `${status}· ${meta}`, `${status}·${meta}`];
|
|
72819
|
+
return candidates.find((candidate) => visibleWidth(candidate) <= width) ?? status;
|
|
72820
|
+
}
|
|
72821
|
+
function nodeCardHeight(stage, opts = {}) {
|
|
72822
|
+
const queuedRows = queuedBadgeCount(opts.queuedMessageCount) > 0 ? 1 : 0;
|
|
72823
|
+
const modelRows = stage.model !== undefined && stage.model !== "" ? 1 : 0;
|
|
72824
|
+
let bodyRows;
|
|
72825
|
+
if (stage.status === "awaiting_input") {
|
|
72826
|
+
bodyRows = 2 + modelRows + queuedRows;
|
|
72827
|
+
} else if (stage.workflowChild !== undefined || stage.workflowChildRun !== undefined) {
|
|
72828
|
+
const innerWidth = Math.max(2, (opts.width ?? NODE_W) - 2);
|
|
72829
|
+
bodyRows = workflowChildRunRows(stage, innerWidth).length + 1 + queuedRows;
|
|
72830
|
+
} else {
|
|
72831
|
+
bodyRows = 2 + modelRows + (metaText(stage) ? 1 : 0) + queuedRows;
|
|
72832
|
+
}
|
|
72833
|
+
return Math.max(NODE_H, bodyRows + 2);
|
|
72834
|
+
}
|
|
72835
|
+
function statusLabel(status) {
|
|
72836
|
+
switch (status) {
|
|
72837
|
+
case "awaiting_input":
|
|
72838
|
+
return "awaiting input";
|
|
72839
|
+
case "completed":
|
|
72840
|
+
return "complete";
|
|
72841
|
+
default:
|
|
72842
|
+
return status.replace(/_/g, " ");
|
|
72843
|
+
}
|
|
72844
|
+
}
|
|
72845
|
+
function truncate(s, maxWidth) {
|
|
72846
|
+
if (maxWidth <= 0)
|
|
72847
|
+
return "";
|
|
72848
|
+
if (visibleWidth(s) <= maxWidth)
|
|
72849
|
+
return s;
|
|
72850
|
+
return truncateToWidth2(s, maxWidth, "…");
|
|
72851
|
+
}
|
|
72852
|
+
function centreColored(content, width, fg, bg, opts = {}) {
|
|
72853
|
+
const safe = truncate(content, width);
|
|
72854
|
+
const safeWidth = visibleWidth(safe);
|
|
72855
|
+
const pad = Math.max(0, width - safeWidth);
|
|
72856
|
+
const left = Math.max(0, Math.floor(pad / 2));
|
|
72857
|
+
const right = Math.max(0, pad - left);
|
|
72858
|
+
const bold = opts.bold ? BOLD : "";
|
|
72859
|
+
return `${bg}${" ".repeat(left)}` + `${hexToAnsi(fg)}${bold}${safe}${RESET}` + `${bg}${" ".repeat(right)}`;
|
|
72860
|
+
}
|
|
72861
|
+
function buildTitleSlot(name, innerWidth, focused, theme, cardBg, compact = false) {
|
|
72862
|
+
const maxName = Math.max(2, compact ? innerWidth - 1 : innerWidth - 4);
|
|
72863
|
+
const safeName = stripTerminalSequences(truncate(name, maxName));
|
|
72864
|
+
if (focused) {
|
|
72865
|
+
const tabText = compact ? safeName : ` ${safeName} `;
|
|
72866
|
+
const styled = `${paint(tabText, theme.surface, {
|
|
72867
|
+
bg: theme.accent,
|
|
72868
|
+
bold: true
|
|
72869
|
+
})}${cardBg}`;
|
|
72870
|
+
return { slot: styled, visibleWidth: visibleWidth(tabText) };
|
|
72871
|
+
}
|
|
72872
|
+
const titleRaw = compact ? safeName : ` ${safeName} `;
|
|
72873
|
+
const styled = `${BOLD}${titleRaw}${RESET}${cardBg}`;
|
|
72874
|
+
return { slot: styled, visibleWidth: visibleWidth(titleRaw) };
|
|
72875
|
+
}
|
|
72876
|
+
function renderNodeCard(stage, opts) {
|
|
72877
|
+
const width = opts.width ?? NODE_W;
|
|
72878
|
+
const height = opts.height ?? nodeCardHeight(stage, opts);
|
|
72879
|
+
const focused = opts.focused ?? false;
|
|
72880
|
+
const phase = opts.pulsePhase ?? 0;
|
|
72881
|
+
const theme = opts.theme;
|
|
72882
|
+
const borderHex = pickBorder(stage.status, focused, phase, theme);
|
|
72883
|
+
const bc = hexToAnsi(borderHex);
|
|
72884
|
+
const bg = DEFAULT_BG;
|
|
72885
|
+
const innerWidth = Math.max(2, width - 2);
|
|
72886
|
+
const child = stage.workflowChild ?? stage.workflowChildRun;
|
|
72887
|
+
const { slot: titleSlot, visibleWidth: titleVisibleWidth } = buildTitleSlot(child === undefined ? stage.name : `↳ ${child.workflow}`, innerWidth, focused, theme, bg, child !== undefined);
|
|
72888
|
+
const titleStart = Math.max(1, Math.floor((innerWidth - titleVisibleWidth) / 2));
|
|
72889
|
+
const titleEnd = titleStart + titleVisibleWidth;
|
|
72890
|
+
const topMiddle = `${bc}${"─".repeat(titleStart)}` + `${titleSlot}${bc}` + `${"─".repeat(Math.max(0, innerWidth - titleEnd))}`;
|
|
72891
|
+
const top = `${bg}${bc}╭${topMiddle}╮${RESET}`;
|
|
72892
|
+
const bottom = `${bg}${bc}╰${"─".repeat(innerWidth)}╯${RESET}`;
|
|
72893
|
+
const bodyText = stage.nodeKind === "tool" ? "durable tool" : stage.status === "blocked" ? blockedBadgeText(stage, opts.stages, innerWidth) : durationText(stage);
|
|
72894
|
+
const bodyHex = durationColor(stage.status, theme);
|
|
72895
|
+
const statusText = `${statusIcon(stage.status)} ${stage.toolStatus ?? statusLabel(stage.status)}`;
|
|
72896
|
+
const queuedCount = queuedBadgeCount(opts.queuedMessageCount);
|
|
72897
|
+
const statusLine = `${bg}${bc}│${RESET}` + centreColored(statusText, innerWidth, bodyHex, bg, {
|
|
72898
|
+
bold: stage.status === "running" || stage.status === "awaiting_input"
|
|
72899
|
+
}) + `${bg}${bc}│${RESET}`;
|
|
72900
|
+
const durLine = `${bg}${bc}│${RESET}` + centreColored(bodyText, innerWidth, bodyHex, bg, {
|
|
72901
|
+
bold: stage.status === "blocked"
|
|
72902
|
+
}) + `${bg}${bc}│${RESET}`;
|
|
72903
|
+
const contentRows = Math.max(0, height - 2);
|
|
72904
|
+
const metaLine = `${bg}${bc}│${RESET}${centreColored(metaText(stage), innerWidth, theme.dim, bg)}${bg}${bc}│${RESET}`;
|
|
72905
|
+
const model = modelText(stage, innerWidth);
|
|
72906
|
+
const modelLine = `${bg}${bc}│${RESET}${centreColored(model, innerWidth, theme.textMuted, bg)}${bg}${bc}│${RESET}`;
|
|
72907
|
+
const childRunLines = workflowChildRunRows(stage, innerWidth).map((row) => `${bg}${bc}│${RESET}${centreColored(row, innerWidth, theme.dim, bg)}${bg}${bc}│${RESET}`);
|
|
72908
|
+
const childMeta = workflowChildMetaText(stage);
|
|
72909
|
+
const childSummary = childMeta === undefined ? undefined : joinCompactStatusMeta(statusText, childMeta, innerWidth);
|
|
72910
|
+
const childSummaryLine = childSummary === undefined ? undefined : `${bg}${bc}│${RESET}` + centreColored(childSummary, innerWidth, bodyHex, bg, {
|
|
72911
|
+
bold: stage.status === "running" || stage.status === "awaiting_input"
|
|
72912
|
+
}) + `${bg}${bc}│${RESET}`;
|
|
72913
|
+
const queuedLines = queuedCount > 0 ? [
|
|
72914
|
+
`${bg}${bc}│${RESET}` + centreColored(queuedBadgeText(queuedCount), innerWidth, theme.info, bg, { bold: true }) + `${bg}${bc}│${RESET}`
|
|
72915
|
+
] : [];
|
|
72916
|
+
const modelLines = model === "" ? [] : [modelLine];
|
|
72917
|
+
const interior = stage.status === "awaiting_input" ? [
|
|
72918
|
+
...modelLines,
|
|
72919
|
+
statusLine,
|
|
72920
|
+
...model === "" && queuedCount === 0 ? [
|
|
72921
|
+
`${bg}${bc}│${RESET}` + centreColored("waiting for response", innerWidth, theme.info, bg) + `${bg}${bc}│${RESET}`
|
|
72922
|
+
] : [],
|
|
72923
|
+
...queuedLines,
|
|
72924
|
+
`${bg}${bc}│${RESET}` + centreColored("↵ enter to respond", innerWidth, theme.dim, bg) + `${bg}${bc}│${RESET}`
|
|
72925
|
+
] : childSummaryLine === undefined ? [durLine, ...modelLines, statusLine, ...metaText(stage) ? [metaLine] : [], ...queuedLines] : [...childRunLines, childSummaryLine, ...queuedLines];
|
|
72926
|
+
while (interior.length < contentRows) {
|
|
72927
|
+
interior.push(`${bg}${bc}│${RESET}${bg}${" ".repeat(innerWidth)}${bg}${bc}│${RESET}`);
|
|
72928
|
+
}
|
|
72929
|
+
if (interior.length > contentRows)
|
|
72930
|
+
interior.length = contentRows;
|
|
72931
|
+
return [top, ...interior, bottom];
|
|
72932
|
+
}
|
|
72933
|
+
|
|
72696
72934
|
// dist/builtin/workflows/src/tui/prompt-card-select.ts
|
|
72697
72935
|
import { SelectList, truncateToWidth as truncateToWidth3 } from "@earendil-works/pi-tui";
|
|
72698
72936
|
function createPromptSelectList(state, theme, maxVisible = 5) {
|
|
@@ -73558,9 +73796,25 @@ class GraphViewState {
|
|
|
73558
73796
|
_needsAnimationTick() {
|
|
73559
73797
|
return this.promptState !== null || this.hasAnimatingStages;
|
|
73560
73798
|
}
|
|
73799
|
+
_nodeHeight(stage) {
|
|
73800
|
+
const baseHeight = nodeCardHeight(stage);
|
|
73801
|
+
if (!this.getStageQueuedMessageCount || nodeCardHeight(stage, { queuedMessageCount: 1 }) === baseHeight) {
|
|
73802
|
+
return baseHeight;
|
|
73803
|
+
}
|
|
73804
|
+
return nodeCardHeight(stage, { queuedMessageCount: this._stageQueuedMessageCount(stage) });
|
|
73805
|
+
}
|
|
73806
|
+
_refreshQueuedNodeHeights() {
|
|
73807
|
+
if (!this.getStageQueuedMessageCount)
|
|
73808
|
+
return;
|
|
73809
|
+
if (!this.cachedLayout.some((node) => (node.height ?? NODE_H) !== this._nodeHeight(node.stage)))
|
|
73810
|
+
return;
|
|
73811
|
+
this.lastBuiltSnapshotVersion = null;
|
|
73812
|
+
this._rebuildLayout();
|
|
73813
|
+
}
|
|
73561
73814
|
_rebuildLayout() {
|
|
73562
73815
|
const version = this.currentSnapshot?.version ?? null;
|
|
73563
73816
|
if (version !== null && version === this.lastBuiltSnapshotVersion) {
|
|
73817
|
+
this._refreshQueuedNodeHeights();
|
|
73564
73818
|
return;
|
|
73565
73819
|
}
|
|
73566
73820
|
const run = this._getCurrentRun();
|
|
@@ -73587,6 +73841,8 @@ class GraphViewState {
|
|
|
73587
73841
|
const next = graphStages[index];
|
|
73588
73842
|
if (!next || node.stage.id !== next.id || node.stage.nodeKind !== next.nodeKind)
|
|
73589
73843
|
return false;
|
|
73844
|
+
if ((node.height ?? NODE_H) !== this._nodeHeight(next))
|
|
73845
|
+
return false;
|
|
73590
73846
|
if (node.stage.parentIds.length !== next.parentIds.length)
|
|
73591
73847
|
return false;
|
|
73592
73848
|
return node.stage.parentIds.every((parentId, parentIndex) => parentId === next.parentIds[parentIndex]);
|
|
@@ -73602,7 +73858,10 @@ class GraphViewState {
|
|
|
73602
73858
|
this.lastBuiltSnapshotVersion = version;
|
|
73603
73859
|
return;
|
|
73604
73860
|
}
|
|
73605
|
-
const nextLayout = computeLayout(graphStages, {
|
|
73861
|
+
const nextLayout = computeLayout(graphStages, {
|
|
73862
|
+
orientation: "vertical",
|
|
73863
|
+
nodeHeight: (stage) => this._nodeHeight(stage)
|
|
73864
|
+
});
|
|
73606
73865
|
this.cachedLayout = nextLayout;
|
|
73607
73866
|
this.cachedDisplayStages = nextLayout.map((node) => node.stage);
|
|
73608
73867
|
this.cachedRenderGeometry = this._buildRenderGeometry(nextLayout);
|
|
@@ -73621,13 +73880,15 @@ class GraphViewState {
|
|
|
73621
73880
|
for (let index = 0;index < layout.length; index++) {
|
|
73622
73881
|
const node = layout[index];
|
|
73623
73882
|
canvasWidth = Math.max(canvasWidth, node.x + NODE_W);
|
|
73624
|
-
|
|
73883
|
+
const bottom = node.y + (node.height ?? NODE_H);
|
|
73884
|
+
totalRows = Math.max(totalRows, bottom);
|
|
73625
73885
|
nodeByStageId.set(node.stage.id, node);
|
|
73626
73886
|
const band = bandsByTop.get(node.y);
|
|
73627
|
-
if (band)
|
|
73887
|
+
if (band) {
|
|
73628
73888
|
band.nodeIndices.push(index);
|
|
73629
|
-
|
|
73630
|
-
|
|
73889
|
+
band.bottom = Math.max(band.bottom, bottom);
|
|
73890
|
+
} else
|
|
73891
|
+
bandsByTop.set(node.y, { top: node.y, bottom, nodeIndices: [index] });
|
|
73631
73892
|
}
|
|
73632
73893
|
const edges = [];
|
|
73633
73894
|
for (const node of layout) {
|
|
@@ -73640,9 +73901,10 @@ class GraphViewState {
|
|
|
73640
73901
|
edges.push({
|
|
73641
73902
|
parentX: parent.x,
|
|
73642
73903
|
parentY: parent.y,
|
|
73904
|
+
parentHeight: parent.height ?? NODE_H,
|
|
73643
73905
|
childX: node.x,
|
|
73644
73906
|
childY: node.y,
|
|
73645
|
-
top: parent.y + NODE_H,
|
|
73907
|
+
top: parent.y + (parent.height ?? NODE_H),
|
|
73646
73908
|
bottom: node.y,
|
|
73647
73909
|
left: Math.min(parentCol, childCol),
|
|
73648
73910
|
right: Math.max(parentCol, childCol) + 1
|
|
@@ -73978,10 +74240,10 @@ class GraphViewRenderHelpers extends GraphViewState {
|
|
|
73978
74240
|
}
|
|
73979
74241
|
this._clampGraphHorizontalScroll(totalCols, viewportCols);
|
|
73980
74242
|
}
|
|
73981
|
-
_plotEdge(canvas, px, py, cx, cy, color) {
|
|
74243
|
+
_plotEdge(canvas, px, py, cx, cy, color, parentHeight = NODE_H) {
|
|
73982
74244
|
const parentCol = px + Math.floor(NODE_W / 2);
|
|
73983
74245
|
const childCol = cx + Math.floor(NODE_W / 2);
|
|
73984
|
-
const parentExitRow = py +
|
|
74246
|
+
const parentExitRow = py + parentHeight;
|
|
73985
74247
|
const childEntryRow = cy - 1;
|
|
73986
74248
|
if (childEntryRow < parentExitRow)
|
|
73987
74249
|
return;
|
|
@@ -74040,214 +74302,6 @@ class GraphViewRenderHelpers extends GraphViewState {
|
|
|
74040
74302
|
}
|
|
74041
74303
|
}
|
|
74042
74304
|
|
|
74043
|
-
// dist/builtin/workflows/src/tui/node-card.ts
|
|
74044
|
-
import { stripTerminalSequences } from "@earendil-works/pi-tui";
|
|
74045
|
-
function pulseT(phase) {
|
|
74046
|
-
return (Math.sin(phase * Math.PI * 2 - Math.PI / 2) + 1) / 2;
|
|
74047
|
-
}
|
|
74048
|
-
function queuedBadgeCount(count) {
|
|
74049
|
-
if (typeof count !== "number" || !Number.isFinite(count))
|
|
74050
|
-
return 0;
|
|
74051
|
-
return Math.max(0, Math.trunc(count));
|
|
74052
|
-
}
|
|
74053
|
-
function queuedBadgeText(count) {
|
|
74054
|
-
return `✉ ${count} queued`;
|
|
74055
|
-
}
|
|
74056
|
-
function pickBorder(status, focused, phase, theme) {
|
|
74057
|
-
switch (status) {
|
|
74058
|
-
case "running":
|
|
74059
|
-
if (focused)
|
|
74060
|
-
return theme.warning;
|
|
74061
|
-
return lerpColor(theme.borderDim, theme.warning, pulseT(phase));
|
|
74062
|
-
case "paused":
|
|
74063
|
-
return theme.warning;
|
|
74064
|
-
case "awaiting_input":
|
|
74065
|
-
if (focused)
|
|
74066
|
-
return theme.info;
|
|
74067
|
-
return lerpColor(theme.borderDim, theme.info, pulseT(phase));
|
|
74068
|
-
case "completed":
|
|
74069
|
-
return theme.success;
|
|
74070
|
-
case "failed":
|
|
74071
|
-
return theme.error;
|
|
74072
|
-
case "blocked":
|
|
74073
|
-
return theme.dim;
|
|
74074
|
-
case "skipped":
|
|
74075
|
-
return theme.dim;
|
|
74076
|
-
default:
|
|
74077
|
-
return focused ? theme.borderActive : theme.borderDim;
|
|
74078
|
-
}
|
|
74079
|
-
}
|
|
74080
|
-
function durationColor(status, theme) {
|
|
74081
|
-
switch (status) {
|
|
74082
|
-
case "running":
|
|
74083
|
-
return theme.warning;
|
|
74084
|
-
case "paused":
|
|
74085
|
-
return theme.warning;
|
|
74086
|
-
case "awaiting_input":
|
|
74087
|
-
return theme.info;
|
|
74088
|
-
case "completed":
|
|
74089
|
-
return theme.success;
|
|
74090
|
-
case "failed":
|
|
74091
|
-
return theme.error;
|
|
74092
|
-
default:
|
|
74093
|
-
return theme.dim;
|
|
74094
|
-
}
|
|
74095
|
-
}
|
|
74096
|
-
function blockedBadgeText(stage, stages, width) {
|
|
74097
|
-
const base = "↑ blocked";
|
|
74098
|
-
const blockedBy = stage.blockedByStageId;
|
|
74099
|
-
if (!blockedBy)
|
|
74100
|
-
return base;
|
|
74101
|
-
const upstream = stages?.find((s) => s.id === blockedBy)?.name ?? blockedBy;
|
|
74102
|
-
const withUpstream = `${base} by ${upstream}`;
|
|
74103
|
-
if (visibleWidth(withUpstream) <= width)
|
|
74104
|
-
return withUpstream;
|
|
74105
|
-
return base;
|
|
74106
|
-
}
|
|
74107
|
-
function durationText(stage) {
|
|
74108
|
-
const elapsed = elapsedStageMs(stage);
|
|
74109
|
-
return elapsed === undefined ? "—" : fmtDuration(elapsed);
|
|
74110
|
-
}
|
|
74111
|
-
function metaText(stage) {
|
|
74112
|
-
if (stage.topologyState === "unavailable")
|
|
74113
|
-
return "topology unavailable";
|
|
74114
|
-
const deps = stage.parentIds.length;
|
|
74115
|
-
const dependencyText = deps === 0 ? "root" : deps === 1 ? "1 dep" : `${deps} deps`;
|
|
74116
|
-
return dependencyText;
|
|
74117
|
-
}
|
|
74118
|
-
function modelText(stage, innerWidth) {
|
|
74119
|
-
const model = stage.model;
|
|
74120
|
-
if (model === undefined || model === "")
|
|
74121
|
-
return "—";
|
|
74122
|
-
const slash = model.lastIndexOf("/");
|
|
74123
|
-
const short = slash >= 0 ? model.slice(slash + 1) : model;
|
|
74124
|
-
const level = stage.thinkingLevel;
|
|
74125
|
-
const showLevel = level !== undefined && level !== "" && level !== "off";
|
|
74126
|
-
const suffix = showLevel ? ` · ${level}` : "";
|
|
74127
|
-
const full = `${short}${suffix}`;
|
|
74128
|
-
if (visibleWidth(full) <= innerWidth)
|
|
74129
|
-
return full;
|
|
74130
|
-
const modelSuffix = short.endsWith("-fast") ? "-fast" : "";
|
|
74131
|
-
const name = modelSuffix ? short.slice(0, -modelSuffix.length) : short;
|
|
74132
|
-
const room = Math.max(1, innerWidth - visibleWidth(modelSuffix + suffix));
|
|
74133
|
-
return `${truncateToWidth2(name, room, "…")}${modelSuffix}${suffix}`;
|
|
74134
|
-
}
|
|
74135
|
-
function workflowChildRunRows(stage, width) {
|
|
74136
|
-
const child = stage.workflowChild ?? stage.workflowChildRun;
|
|
74137
|
-
if (child === undefined)
|
|
74138
|
-
return [];
|
|
74139
|
-
return wrapIdentifierLines(child.runId, Math.max(1, width), "run ", "").map((row) => `${row.prefix}${row.chunk}`);
|
|
74140
|
-
}
|
|
74141
|
-
function workflowChildMetaText(stage) {
|
|
74142
|
-
const completed = stage.workflowChild;
|
|
74143
|
-
if (completed !== undefined) {
|
|
74144
|
-
const outputCount = completed.outputCount ?? Object.keys(completed.outputs).length;
|
|
74145
|
-
return outputCount === 1 ? "1 out" : `${outputCount} outs`;
|
|
74146
|
-
}
|
|
74147
|
-
if (stage.workflowChildRun !== undefined)
|
|
74148
|
-
return "live";
|
|
74149
|
-
return;
|
|
74150
|
-
}
|
|
74151
|
-
function joinCompactStatusMeta(status, meta, width) {
|
|
74152
|
-
const candidates = [`${status} · ${meta}`, `${status} ·${meta}`, `${status}· ${meta}`, `${status}·${meta}`];
|
|
74153
|
-
return candidates.find((candidate) => visibleWidth(candidate) <= width) ?? meta;
|
|
74154
|
-
}
|
|
74155
|
-
function statusLabel(status) {
|
|
74156
|
-
switch (status) {
|
|
74157
|
-
case "awaiting_input":
|
|
74158
|
-
return "awaiting input";
|
|
74159
|
-
case "completed":
|
|
74160
|
-
return "complete";
|
|
74161
|
-
default:
|
|
74162
|
-
return status.replace(/_/g, " ");
|
|
74163
|
-
}
|
|
74164
|
-
}
|
|
74165
|
-
function truncate(s, maxWidth) {
|
|
74166
|
-
if (maxWidth <= 0)
|
|
74167
|
-
return "";
|
|
74168
|
-
if (visibleWidth(s) <= maxWidth)
|
|
74169
|
-
return s;
|
|
74170
|
-
return truncateToWidth2(s, maxWidth, "…");
|
|
74171
|
-
}
|
|
74172
|
-
function centreColored(content, width, fg, bg, opts = {}) {
|
|
74173
|
-
const safe = truncate(content, width);
|
|
74174
|
-
const safeWidth = visibleWidth(safe);
|
|
74175
|
-
const pad = Math.max(0, width - safeWidth);
|
|
74176
|
-
const left = Math.max(0, Math.floor(pad / 2));
|
|
74177
|
-
const right = Math.max(0, pad - left);
|
|
74178
|
-
const bold = opts.bold ? BOLD : "";
|
|
74179
|
-
return `${bg}${" ".repeat(left)}` + `${hexToAnsi(fg)}${bold}${safe}${RESET}` + `${bg}${" ".repeat(right)}`;
|
|
74180
|
-
}
|
|
74181
|
-
function buildTitleSlot(name, innerWidth, focused, theme, cardBg, compact = false) {
|
|
74182
|
-
const maxName = Math.max(2, compact ? innerWidth - 1 : innerWidth - 4);
|
|
74183
|
-
const safeName = stripTerminalSequences(truncate(name, maxName));
|
|
74184
|
-
if (focused) {
|
|
74185
|
-
const tabText = compact ? safeName : ` ${safeName} `;
|
|
74186
|
-
const styled = `${paint(tabText, theme.surface, {
|
|
74187
|
-
bg: theme.accent,
|
|
74188
|
-
bold: true
|
|
74189
|
-
})}${cardBg}`;
|
|
74190
|
-
return { slot: styled, visibleWidth: visibleWidth(tabText) };
|
|
74191
|
-
}
|
|
74192
|
-
const titleRaw = compact ? safeName : ` ${safeName} `;
|
|
74193
|
-
const styled = `${BOLD}${titleRaw}${RESET}${cardBg}`;
|
|
74194
|
-
return { slot: styled, visibleWidth: visibleWidth(titleRaw) };
|
|
74195
|
-
}
|
|
74196
|
-
function renderNodeCard(stage, opts) {
|
|
74197
|
-
const width = opts.width ?? NODE_W;
|
|
74198
|
-
const height = opts.height ?? NODE_H;
|
|
74199
|
-
const focused = opts.focused ?? false;
|
|
74200
|
-
const phase = opts.pulsePhase ?? 0;
|
|
74201
|
-
const theme = opts.theme;
|
|
74202
|
-
const borderHex = pickBorder(stage.status, focused, phase, theme);
|
|
74203
|
-
const bc = hexToAnsi(borderHex);
|
|
74204
|
-
const bg = DEFAULT_BG;
|
|
74205
|
-
const innerWidth = Math.max(2, width - 2);
|
|
74206
|
-
const child = stage.workflowChild ?? stage.workflowChildRun;
|
|
74207
|
-
const { slot: titleSlot, visibleWidth: titleVisibleWidth } = buildTitleSlot(child === undefined ? stage.name : `↳ ${child.workflow}`, innerWidth, focused, theme, bg, child !== undefined);
|
|
74208
|
-
const titleStart = Math.max(1, Math.floor((innerWidth - titleVisibleWidth) / 2));
|
|
74209
|
-
const titleEnd = titleStart + titleVisibleWidth;
|
|
74210
|
-
const topMiddle = `${bc}${"─".repeat(titleStart)}` + `${titleSlot}${bc}` + `${"─".repeat(Math.max(0, innerWidth - titleEnd))}`;
|
|
74211
|
-
const top = `${bg}${bc}╭${topMiddle}╮${RESET}`;
|
|
74212
|
-
const bottom = `${bg}${bc}╰${"─".repeat(innerWidth)}╯${RESET}`;
|
|
74213
|
-
const bodyText = stage.nodeKind === "tool" ? "durable tool" : stage.status === "blocked" ? blockedBadgeText(stage, opts.stages, innerWidth) : durationText(stage);
|
|
74214
|
-
const bodyHex = durationColor(stage.status, theme);
|
|
74215
|
-
const statusText = `${statusIcon(stage.status)} ${stage.toolStatus ?? statusLabel(stage.status)}`;
|
|
74216
|
-
const statusLine = `${bg}${bc}│${RESET}` + centreColored(statusText, innerWidth, bodyHex, bg, {
|
|
74217
|
-
bold: stage.status === "running" || stage.status === "awaiting_input"
|
|
74218
|
-
}) + `${bg}${bc}│${RESET}`;
|
|
74219
|
-
const durLine = `${bg}${bc}│${RESET}` + centreColored(bodyText, innerWidth, bodyHex, bg, {
|
|
74220
|
-
bold: stage.status === "blocked"
|
|
74221
|
-
}) + `${bg}${bc}│${RESET}`;
|
|
74222
|
-
const contentRows = Math.max(0, height - 2);
|
|
74223
|
-
const metaLine = `${bg}${bc}│${RESET}${centreColored(metaText(stage), innerWidth, theme.dim, bg)}${bg}${bc}│${RESET}`;
|
|
74224
|
-
const modelLine = `${bg}${bc}│${RESET}${centreColored(modelText(stage, innerWidth), innerWidth, theme.textMuted, bg)}${bg}${bc}│${RESET}`;
|
|
74225
|
-
const childRunLines = workflowChildRunRows(stage, innerWidth).map((row) => `${bg}${bc}│${RESET}${centreColored(row, innerWidth, theme.dim, bg)}${bg}${bc}│${RESET}`);
|
|
74226
|
-
const queuedCount = queuedBadgeCount(opts.queuedMessageCount);
|
|
74227
|
-
const childMeta = workflowChildMetaText(stage);
|
|
74228
|
-
const childSummary = childMeta === undefined ? undefined : joinCompactStatusMeta(statusText, queuedCount > 0 ? queuedBadgeText(queuedCount) : childMeta, innerWidth);
|
|
74229
|
-
const childSummaryLine = childSummary === undefined ? undefined : `${bg}${bc}│${RESET}` + centreColored(childSummary, innerWidth, bodyHex, bg, {
|
|
74230
|
-
bold: stage.status === "running" || stage.status === "awaiting_input"
|
|
74231
|
-
}) + `${bg}${bc}│${RESET}`;
|
|
74232
|
-
const interior = stage.status === "awaiting_input" ? [
|
|
74233
|
-
statusLine,
|
|
74234
|
-
`${bg}${bc}│${RESET}` + centreColored("waiting for response", innerWidth, theme.info, bg) + `${bg}${bc}│${RESET}`,
|
|
74235
|
-
`${bg}${bc}│${RESET}` + centreColored("↵ enter to respond", innerWidth, theme.dim, bg) + `${bg}${bc}│${RESET}`,
|
|
74236
|
-
modelLine
|
|
74237
|
-
] : childSummaryLine === undefined ? [durLine, statusLine, modelLine, metaLine] : [...childRunLines, childSummaryLine];
|
|
74238
|
-
const preferredBadgeRow = stage.status === "awaiting_input" ? 1 : childSummaryLine === undefined ? interior.length - 1 : -1;
|
|
74239
|
-
while (interior.length < contentRows) {
|
|
74240
|
-
interior.push(`${bg}${bc}│${RESET}${bg}${" ".repeat(innerWidth)}${bg}${bc}│${RESET}`);
|
|
74241
|
-
}
|
|
74242
|
-
if (interior.length > contentRows)
|
|
74243
|
-
interior.length = contentRows;
|
|
74244
|
-
if (queuedCount > 0 && interior.length > 0 && preferredBadgeRow >= 0) {
|
|
74245
|
-
const badgeRow = preferredBadgeRow < interior.length ? preferredBadgeRow : interior.length - 1;
|
|
74246
|
-
interior[badgeRow] = `${bg}${bc}│${RESET}` + centreColored(queuedBadgeText(queuedCount), innerWidth, theme.info, bg, { bold: true }) + `${bg}${bc}│${RESET}`;
|
|
74247
|
-
}
|
|
74248
|
-
return [top, ...interior, bottom];
|
|
74249
|
-
}
|
|
74250
|
-
|
|
74251
74305
|
// dist/builtin/workflows/src/tui/graph-view-graph-render.ts
|
|
74252
74306
|
class GraphViewGraphRenderer extends GraphViewRenderHelpers {
|
|
74253
74307
|
_renderGraph(width, viewportTop, viewportRows, renderRun) {
|
|
@@ -74292,7 +74346,7 @@ class GraphViewGraphRenderer extends GraphViewRenderHelpers {
|
|
|
74292
74346
|
if (edge.bottom <= safeTop || edge.top >= viewportBottom || edge.right <= viewportLeft || edge.left >= viewportRight) {
|
|
74293
74347
|
continue;
|
|
74294
74348
|
}
|
|
74295
|
-
this._plotEdge(edgeCanvas, edge.parentX, edge.parentY, edge.childX, edge.childY, edgeColor);
|
|
74349
|
+
this._plotEdge(edgeCanvas, edge.parentX, edge.parentY, edge.childX, edge.childY, edgeColor, edge.parentHeight);
|
|
74296
74350
|
}
|
|
74297
74351
|
const edgeLines = edgeCanvas.toLines();
|
|
74298
74352
|
const placements = new Map;
|
|
@@ -74306,7 +74360,7 @@ class GraphViewGraphRenderer extends GraphViewRenderHelpers {
|
|
|
74306
74360
|
continue;
|
|
74307
74361
|
const cardLines = renderNodeCard(node.stage, {
|
|
74308
74362
|
width: NODE_W,
|
|
74309
|
-
height: NODE_H,
|
|
74363
|
+
height: node.height ?? NODE_H,
|
|
74310
74364
|
focused: ni === this.focusedIndex,
|
|
74311
74365
|
pulsePhase,
|
|
74312
74366
|
theme: this.graphTheme,
|
|
@@ -74363,7 +74417,7 @@ class GraphViewGraphRenderer extends GraphViewRenderHelpers {
|
|
|
74363
74417
|
for (const index of band.nodeIndices) {
|
|
74364
74418
|
const node = this.cachedLayout[index];
|
|
74365
74419
|
const top = graphStartRow + node.y - viewportTop;
|
|
74366
|
-
const bottom = top + NODE_H;
|
|
74420
|
+
const bottom = top + (node.height ?? NODE_H);
|
|
74367
74421
|
const left = viewport.leftMargin + node.x - this.graphScrollColOffset;
|
|
74368
74422
|
const right = left + NODE_W;
|
|
74369
74423
|
const clippedTop = Math.max(visibleTop, top);
|
|
@@ -75071,6 +75125,7 @@ class GraphViewRenderer extends GraphViewGraphRenderer {
|
|
|
75071
75125
|
render(width) {
|
|
75072
75126
|
if (this.mode === "widget")
|
|
75073
75127
|
return this._renderWidget(width);
|
|
75128
|
+
this._refreshQueuedNodeHeights();
|
|
75074
75129
|
return this._renderOverlay(width);
|
|
75075
75130
|
}
|
|
75076
75131
|
dispose() {
|
|
@@ -75183,8 +75238,8 @@ class GraphViewRenderer extends GraphViewGraphRenderer {
|
|
|
75183
75238
|
let next = scrollView.scrollTop;
|
|
75184
75239
|
if (node.y < next)
|
|
75185
75240
|
next = node.y;
|
|
75186
|
-
else if (node.y + NODE_H > next + viewportRows)
|
|
75187
|
-
next = node.y + NODE_H - viewportRows;
|
|
75241
|
+
else if (node.y + (node.height ?? NODE_H) > next + viewportRows)
|
|
75242
|
+
next = node.y + (node.height ?? NODE_H) - viewportRows;
|
|
75188
75243
|
scrollView.scrollTo(next);
|
|
75189
75244
|
const graphInner = Math.max(1, width - 4);
|
|
75190
75245
|
const leftMargin = Math.max(2, this.cachedRenderGeometry.canvasWidth <= graphInner ? Math.floor((graphInner - this.cachedRenderGeometry.canvasWidth) / 2) : 2);
|
|
@@ -79038,7 +79093,9 @@ function plainCollapsed(counts, activeTools) {
|
|
|
79038
79093
|
const tools = activeTools > 0 ? ` · ${activeTools} tool${activeTools === 1 ? "" : "s"}` : "";
|
|
79039
79094
|
return ` ▾ ${total} background · ${counts.active} ●${paused}${quit}${blocked}${tools}`;
|
|
79040
79095
|
}
|
|
79041
|
-
function buildThemedWidgetLines(snap, piTheme, width = 120, now = Date.now()) {
|
|
79096
|
+
function buildThemedWidgetLines(snap, piTheme, width = 120, now = Date.now(), layout) {
|
|
79097
|
+
if (layout)
|
|
79098
|
+
layout.runs = [];
|
|
79042
79099
|
const display = selectDisplayRuns(snap, now);
|
|
79043
79100
|
if (display.length === 0)
|
|
79044
79101
|
return [];
|
|
@@ -79057,7 +79114,7 @@ function buildThemedWidgetLines(snap, piTheme, width = 120, now = Date.now()) {
|
|
|
79057
79114
|
const activeTools = display.filter(isActive).reduce((total, run) => total + activeToolNodes(run).length, 0);
|
|
79058
79115
|
if (width < COLLAPSED_BREAKPOINT_COLS) {
|
|
79059
79116
|
return [
|
|
79060
|
-
themed ? themedCollapsed(visibleCounts, activeTools, graphTheme) : plainCollapsed(visibleCounts, activeTools)
|
|
79117
|
+
truncateToWidth2(themed ? themedCollapsed(visibleCounts, activeTools, graphTheme) : plainCollapsed(visibleCounts, activeTools), width, "…")
|
|
79061
79118
|
];
|
|
79062
79119
|
}
|
|
79063
79120
|
const total = display.length;
|
|
@@ -79071,6 +79128,11 @@ function buildThemedWidgetLines(snap, piTheme, width = 120, now = Date.now()) {
|
|
|
79071
79128
|
const run = display[i];
|
|
79072
79129
|
const runLines = themed ? themedRunLines(run, now, graphTheme, snap.runs, width, expandGraph) : plainRunLines(run, now, snap.runs, width, expandGraph);
|
|
79073
79130
|
body.push(...runLines);
|
|
79131
|
+
layout?.runs.push({
|
|
79132
|
+
id: run.id,
|
|
79133
|
+
start: body.length - runLines.length + 1,
|
|
79134
|
+
end: body.length + (i === display.length - 1 ? 2 : 1)
|
|
79135
|
+
});
|
|
79074
79136
|
if (i < display.length - 1)
|
|
79075
79137
|
body.push("");
|
|
79076
79138
|
}
|
|
@@ -79083,7 +79145,95 @@ function buildThemedWidgetLines(snap, piTheme, width = 120, now = Date.now()) {
|
|
|
79083
79145
|
});
|
|
79084
79146
|
}
|
|
79085
79147
|
|
|
79148
|
+
// dist/builtin/workflows/src/tui/widget-viewport.ts
|
|
79149
|
+
import { ScrollableComponentViewport } from "@bastani/atomic";
|
|
79150
|
+
var WORKFLOW_WIDGET_MAX_ROWS = 10;
|
|
79151
|
+
|
|
79152
|
+
class WorkflowWidgetViewport {
|
|
79153
|
+
content;
|
|
79154
|
+
terminalRows;
|
|
79155
|
+
requestRender;
|
|
79156
|
+
getRunRows;
|
|
79157
|
+
viewport = new ScrollableComponentViewport;
|
|
79158
|
+
lines = [];
|
|
79159
|
+
scrollable = false;
|
|
79160
|
+
runRows = [];
|
|
79161
|
+
constructor(content, terminalRows, requestRender, getRunRows) {
|
|
79162
|
+
this.content = content;
|
|
79163
|
+
this.terminalRows = terminalRows;
|
|
79164
|
+
this.requestRender = requestRender;
|
|
79165
|
+
this.getRunRows = getRunRows;
|
|
79166
|
+
this.viewport.setComponents([{ render: () => this.lines, invalidate() {} }]);
|
|
79167
|
+
this.viewport.scrollTo(0);
|
|
79168
|
+
}
|
|
79169
|
+
scroll(direction) {
|
|
79170
|
+
if (!this.scrollable)
|
|
79171
|
+
return;
|
|
79172
|
+
this.viewport.scrollBy(direction);
|
|
79173
|
+
this.requestRender();
|
|
79174
|
+
}
|
|
79175
|
+
render(width) {
|
|
79176
|
+
const cap = Math.max(1, Math.min(WORKFLOW_WIDGET_MAX_ROWS, Math.floor(this.terminalRows() / 3)));
|
|
79177
|
+
const nextLines = this.content.render(width);
|
|
79178
|
+
const nextRunRows = this.getRunRows?.() ?? [];
|
|
79179
|
+
if (this.getRunRows && nextLines.length === 1 && nextRunRows.length === 0) {
|
|
79180
|
+
this.scrollable = false;
|
|
79181
|
+
return nextLines;
|
|
79182
|
+
}
|
|
79183
|
+
const previousLineCount = this.lines.length;
|
|
79184
|
+
const offset = this.viewport.getMaxScroll() - this.viewport.getScrollFromBottom();
|
|
79185
|
+
const anchoredOffset = this.getRunRows ? this.resolveAnchor(offset, nextRunRows) : undefined;
|
|
79186
|
+
this.lines = nextLines;
|
|
79187
|
+
this.runRows = nextRunRows;
|
|
79188
|
+
if (anchoredOffset !== undefined) {
|
|
79189
|
+
this.viewport.scrollTo(anchoredOffset);
|
|
79190
|
+
} else if (previousLineCount <= 1 || this.lines.length < previousLineCount && this.lines.length <= cap) {
|
|
79191
|
+
this.viewport.scrollTo(0);
|
|
79192
|
+
}
|
|
79193
|
+
this.scrollable = this.lines.length > 1;
|
|
79194
|
+
this.viewport.setVisibleRows(1);
|
|
79195
|
+
this.viewport.render(width);
|
|
79196
|
+
if (!this.scrollable)
|
|
79197
|
+
return this.lines;
|
|
79198
|
+
const first = this.viewport.getMaxScroll() - this.viewport.getScrollFromBottom() + 1;
|
|
79199
|
+
const visible = this.lines.slice(first - 1, first - 1 + Math.max(1, cap - 1));
|
|
79200
|
+
if (cap === 1)
|
|
79201
|
+
return visible;
|
|
79202
|
+
const hint = ` ${first}–${first + visible.length - 1}/${this.lines.length} · Alt+PgUp/PgDn scroll workflows`;
|
|
79203
|
+
return [...visible, truncateToWidth2(hint, width, "…")];
|
|
79204
|
+
}
|
|
79205
|
+
resolveAnchor(offset, next) {
|
|
79206
|
+
if (offset === 0 || next.length === 0)
|
|
79207
|
+
return 0;
|
|
79208
|
+
const index = this.runRows.findIndex((run) => offset >= run.start - 1 && offset < run.end);
|
|
79209
|
+
const anchor = this.runRows[index];
|
|
79210
|
+
if (!anchor)
|
|
79211
|
+
return 0;
|
|
79212
|
+
const byId = new Map(next.map((run) => [run.id, run]));
|
|
79213
|
+
const retained = byId.get(anchor.id);
|
|
79214
|
+
if (retained)
|
|
79215
|
+
return Math.min(retained.end - 1, retained.start + offset - anchor.start);
|
|
79216
|
+
const neighbours = [...this.runRows.slice(index + 1), ...this.runRows.slice(0, index).reverse()];
|
|
79217
|
+
for (const neighbour of neighbours) {
|
|
79218
|
+
const survivor = byId.get(neighbour.id);
|
|
79219
|
+
if (survivor)
|
|
79220
|
+
return survivor.start;
|
|
79221
|
+
}
|
|
79222
|
+
return 0;
|
|
79223
|
+
}
|
|
79224
|
+
invalidate() {
|
|
79225
|
+
this.content.invalidate?.();
|
|
79226
|
+
}
|
|
79227
|
+
dispose() {
|
|
79228
|
+
this.content.dispose?.();
|
|
79229
|
+
}
|
|
79230
|
+
}
|
|
79231
|
+
|
|
79086
79232
|
// dist/builtin/workflows/src/tui/store-widget-installer.ts
|
|
79233
|
+
var widgetViewports = new WeakMap;
|
|
79234
|
+
function scrollStoreWidget(storeInstance, direction) {
|
|
79235
|
+
widgetViewports.get(storeInstance)?.scroll(direction);
|
|
79236
|
+
}
|
|
79087
79237
|
var defaultTimerApi = {
|
|
79088
79238
|
setTimeout: (handler, delayMs) => setTimeout(handler, delayMs),
|
|
79089
79239
|
clearTimeout: (handle) => clearTimeout(handle)
|
|
@@ -79114,9 +79264,19 @@ function installStoreWidget(pi, storeInstance, timers = defaultTimerApi) {
|
|
|
79114
79264
|
const setWidget = ui.setWidget;
|
|
79115
79265
|
const requestRender = ui.requestRender;
|
|
79116
79266
|
const onWidgetRelease = ui.onWidgetRelease;
|
|
79267
|
+
const layout = { runs: [] };
|
|
79117
79268
|
const controller = installReactiveWidget({
|
|
79118
79269
|
ui: {
|
|
79119
|
-
setWidget: (key, factory, opts) =>
|
|
79270
|
+
setWidget: (key, factory, opts) => {
|
|
79271
|
+
if (!factory)
|
|
79272
|
+
widgetViewports.delete(storeInstance);
|
|
79273
|
+
setWidget.call(ui, key, factory ? (tui, theme) => {
|
|
79274
|
+
const host = tui;
|
|
79275
|
+
const viewport = new WorkflowWidgetViewport(factory(tui, theme), () => host?.terminal?.rows ?? 30, () => requestRender ? requestRender.call(ui) : host?.requestRender?.(), () => layout.runs);
|
|
79276
|
+
widgetViewports.set(storeInstance, viewport);
|
|
79277
|
+
return viewport;
|
|
79278
|
+
} : undefined, opts);
|
|
79279
|
+
},
|
|
79120
79280
|
...requestRender ? { requestRender: () => requestRender.call(ui) } : {},
|
|
79121
79281
|
...onWidgetRelease ? { onWidgetRelease: (key, listener) => onWidgetRelease.call(ui, key, listener) } : {}
|
|
79122
79282
|
},
|
|
@@ -79126,7 +79286,7 @@ function installStoreWidget(pi, storeInstance, timers = defaultTimerApi) {
|
|
|
79126
79286
|
getSnapshot: () => liveWidgetSnapshot(storeInstance),
|
|
79127
79287
|
subscribe: (listener) => subscribeStoreInvalidation(storeInstance, listener),
|
|
79128
79288
|
getPreviewLines: (snap, now) => buildThemedWidgetLines(snap, undefined, 120, now),
|
|
79129
|
-
render: (snap, { theme, width, now }) => buildThemedWidgetLines(snap, theme, width, now),
|
|
79289
|
+
render: (snap, { theme, width, now }) => buildThemedWidgetLines(snap, theme, width, now, layout),
|
|
79130
79290
|
getNextRefreshDelayMs: (snap, now) => nextWidgetRefreshDelayMs(snap, now),
|
|
79131
79291
|
requestRenderOnStateNoop: false,
|
|
79132
79292
|
isStaleError: isStaleExtensionContextError4,
|
|
@@ -79134,7 +79294,10 @@ function installStoreWidget(pi, storeInstance, timers = defaultTimerApi) {
|
|
|
79134
79294
|
reportWidgetFailure(ui, `Workflow progress widget could not mount: ${error.message}`);
|
|
79135
79295
|
}
|
|
79136
79296
|
});
|
|
79137
|
-
return () =>
|
|
79297
|
+
return () => {
|
|
79298
|
+
widgetViewports.delete(storeInstance);
|
|
79299
|
+
controller.dispose();
|
|
79300
|
+
};
|
|
79138
79301
|
}
|
|
79139
79302
|
function installToolExecutionHooks(pi, storeInstance) {
|
|
79140
79303
|
const eventBusOn = pi.events?.on;
|
|
@@ -117680,6 +117843,14 @@ function registerWorkflowShortcut(pi, overlay) {
|
|
|
117680
117843
|
description: "Open workflow orchestrator pane",
|
|
117681
117844
|
handler: openPane
|
|
117682
117845
|
});
|
|
117846
|
+
pi.registerShortcut("alt+pageUp", {
|
|
117847
|
+
description: "Scroll background workflows up",
|
|
117848
|
+
handler: () => scrollStoreWidget(store, -1)
|
|
117849
|
+
});
|
|
117850
|
+
pi.registerShortcut("alt+pageDown", {
|
|
117851
|
+
description: "Scroll background workflows down",
|
|
117852
|
+
handler: () => scrollStoreWidget(store, 1)
|
|
117853
|
+
});
|
|
117683
117854
|
}
|
|
117684
117855
|
function registerIntercomControl(pi, intercomControlRef) {
|
|
117685
117856
|
intercomControlRef.current = subscribeIntercomControl(pi, buildIntercomCallbacks({
|