cvc-tui 0.4.0 → 0.4.2
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/dist/entry.js +71148 -61
- package/package.json +2 -2
- package/dist/app/completion.js +0 -102
- package/dist/app/createGatewayEventHandler.js +0 -508
- package/dist/app/createSlashHandler.js +0 -101
- package/dist/app/delegationStore.js +0 -51
- package/dist/app/gatewayContext.js +0 -17
- package/dist/app/historyStore.js +0 -123
- package/dist/app/inputBuffer.js +0 -120
- package/dist/app/inputSelectionStore.js +0 -8
- package/dist/app/inputStore.js +0 -28
- package/dist/app/interfaces.js +0 -6
- package/dist/app/overlayStore.js +0 -40
- package/dist/app/promptStore.js +0 -44
- package/dist/app/queueStore.js +0 -25
- package/dist/app/scroll.js +0 -44
- package/dist/app/setupHandoff.js +0 -28
- package/dist/app/slash/commands/core.js +0 -479
- package/dist/app/slash/commands/debug.js +0 -44
- package/dist/app/slash/commands/ops.js +0 -498
- package/dist/app/slash/commands/session.js +0 -431
- package/dist/app/slash/commands/setup.js +0 -20
- package/dist/app/slash/commands/toggles.js +0 -40
- package/dist/app/slash/registry.js +0 -18
- package/dist/app/slash/types.js +0 -1
- package/dist/app/spawnHistoryStore.js +0 -105
- package/dist/app/turnController.js +0 -650
- package/dist/app/turnStore.js +0 -48
- package/dist/app/uiStore.js +0 -36
- package/dist/app/useComposerState.js +0 -265
- package/dist/app/useConfigSync.js +0 -144
- package/dist/app/useInputHandlers.js +0 -403
- package/dist/app/useLongRunToolCharms.js +0 -50
- package/dist/app/useMainApp.js +0 -629
- package/dist/app/useSessionLifecycle.js +0 -175
- package/dist/app/useSubmission.js +0 -287
- package/dist/app.js +0 -15
- package/dist/banner.js +0 -57
- package/dist/components/agentsOverlay.js +0 -474
- package/dist/components/appChrome.js +0 -252
- package/dist/components/appLayout.js +0 -121
- package/dist/components/appOverlays.js +0 -65
- package/dist/components/branding.js +0 -97
- package/dist/components/fpsOverlay.js +0 -22
- package/dist/components/helpHint.js +0 -21
- package/dist/components/markdown.js +0 -501
- package/dist/components/maskedPrompt.js +0 -12
- package/dist/components/messageLine.js +0 -82
- package/dist/components/modelPicker.js +0 -254
- package/dist/components/overlayControls.js +0 -30
- package/dist/components/overlays/confirmPrompt.js +0 -25
- package/dist/components/overlays/helpOverlay.js +0 -76
- package/dist/components/overlays/historySearch.js +0 -49
- package/dist/components/overlays/modelPicker.js +0 -60
- package/dist/components/overlays/overlayUtils.js +0 -19
- package/dist/components/overlays/secretPrompt.js +0 -36
- package/dist/components/overlays/sessionPicker.js +0 -93
- package/dist/components/overlays/skillsHub.js +0 -71
- package/dist/components/prompts.js +0 -95
- package/dist/components/queuedMessages.js +0 -24
- package/dist/components/sessionPicker.js +0 -130
- package/dist/components/skillsHub.js +0 -165
- package/dist/components/streamingAssistant.js +0 -35
- package/dist/components/streamingMarkdown.js +0 -144
- package/dist/components/textInput.js +0 -794
- package/dist/components/themed.js +0 -12
- package/dist/components/thinking.js +0 -496
- package/dist/components/todoPanel.js +0 -40
- package/dist/components/transcript.js +0 -22
- package/dist/config/env.js +0 -18
- package/dist/config/limits.js +0 -22
- package/dist/config/timing.js +0 -18
- package/dist/content/charms.js +0 -5
- package/dist/content/faces.js +0 -21
- package/dist/content/fortunes.js +0 -29
- package/dist/content/hotkeys.js +0 -38
- package/dist/content/placeholders.js +0 -15
- package/dist/content/setup.js +0 -14
- package/dist/content/verbs.js +0 -41
- package/dist/domain/details.js +0 -53
- package/dist/domain/messages.js +0 -63
- package/dist/domain/paths.js +0 -16
- package/dist/domain/providers.js +0 -11
- package/dist/domain/roles.js +0 -6
- package/dist/domain/slash.js +0 -11
- package/dist/domain/usage.js +0 -1
- package/dist/domain/viewport.js +0 -33
- package/dist/gateway/client.js +0 -312
- package/dist/gatewayClient.js +0 -574
- package/dist/gatewayTypes.js +0 -1
- package/dist/hooks/useCompletion.js +0 -86
- package/dist/hooks/useGitBranch.js +0 -58
- package/dist/hooks/useInputHistory.js +0 -12
- package/dist/hooks/useQueue.js +0 -57
- package/dist/hooks/useVirtualHistory.js +0 -401
- package/dist/lib/circularBuffer.js +0 -43
- package/dist/lib/clipboard.js +0 -126
- package/dist/lib/editor.js +0 -41
- package/dist/lib/editor.test.js +0 -58
- package/dist/lib/emoji.js +0 -49
- package/dist/lib/externalCli.js +0 -11
- package/dist/lib/forceTruecolor.js +0 -26
- package/dist/lib/fpsStore.js +0 -36
- package/dist/lib/gracefulExit.js +0 -29
- package/dist/lib/history.js +0 -69
- package/dist/lib/inputMetrics.js +0 -143
- package/dist/lib/liveProgress.js +0 -51
- package/dist/lib/liveProgress.test.js +0 -89
- package/dist/lib/mathUnicode.js +0 -685
- package/dist/lib/memory.js +0 -123
- package/dist/lib/memoryMonitor.js +0 -76
- package/dist/lib/messages.js +0 -3
- package/dist/lib/messages.test.js +0 -25
- package/dist/lib/osc52.js +0 -53
- package/dist/lib/perfPane.js +0 -94
- package/dist/lib/platform.js +0 -312
- package/dist/lib/precisionWheel.js +0 -25
- package/dist/lib/reasoning.js +0 -39
- package/dist/lib/rpc.js +0 -26
- package/dist/lib/subagentTree.js +0 -287
- package/dist/lib/syntax.js +0 -89
- package/dist/lib/terminalModes.js +0 -46
- package/dist/lib/terminalParity.js +0 -48
- package/dist/lib/terminalSetup.js +0 -321
- package/dist/lib/text.js +0 -203
- package/dist/lib/text.test.js +0 -18
- package/dist/lib/todo.js +0 -2
- package/dist/lib/todo.test.js +0 -22
- package/dist/lib/viewportStore.js +0 -82
- package/dist/lib/virtualHeights.js +0 -61
- package/dist/lib/wheelAccel.js +0 -143
- package/dist/theme.js +0 -398
- package/dist/types.js +0 -1
|
@@ -1,474 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
// @ts-nocheck
|
|
3
|
-
// SPDX-License-Identifier: MIT
|
|
4
|
-
// Ported from CVC Agent (https://github.com/NousResearch/cvc)
|
|
5
|
-
// Original Copyright (c) 2025 Nous Research. CVC adaptations (c) 2026 Jai Kumar Meena.
|
|
6
|
-
import { Box, NoSelect, ScrollBox, Text, useInput, useStdout } from '@cvc/ink';
|
|
7
|
-
import { useStore } from '@nanostores/react';
|
|
8
|
-
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
9
|
-
import { $delegationState, $overlaySectionsOpen, applyDelegationStatus, toggleOverlaySection } from '../app/delegationStore.js';
|
|
10
|
-
import { patchOverlayState } from '../app/overlayStore.js';
|
|
11
|
-
import { $spawnDiff, $spawnHistory, clearDiffPair } from '../app/spawnHistoryStore.js';
|
|
12
|
-
import { useTurnSelector } from '../app/turnStore.js';
|
|
13
|
-
import { asRpcResult } from '../lib/rpc.js';
|
|
14
|
-
import { buildSubagentTree, descendantIds, flattenTree, fmtCost, fmtDuration, fmtTokens, formatSummary, hotnessBucket, peakHotness, sparkline, topLevelSubagents, treeTotals, widthByDepth } from '../lib/subagentTree.js';
|
|
15
|
-
import { compactPreview } from '../lib/text.js';
|
|
16
|
-
const SORT_ORDER = ['depth-first', 'tools-desc', 'duration-desc', 'status'];
|
|
17
|
-
const FILTER_ORDER = ['all', 'running', 'failed', 'leaf'];
|
|
18
|
-
const SORT_LABEL = {
|
|
19
|
-
'depth-first': 'spawn order',
|
|
20
|
-
'duration-desc': 'slowest',
|
|
21
|
-
status: 'status',
|
|
22
|
-
'tools-desc': 'busiest'
|
|
23
|
-
};
|
|
24
|
-
const FILTER_LABEL = {
|
|
25
|
-
all: 'all',
|
|
26
|
-
failed: 'failed',
|
|
27
|
-
leaf: 'leaves',
|
|
28
|
-
running: 'running'
|
|
29
|
-
};
|
|
30
|
-
const STATUS_RANK = {
|
|
31
|
-
failed: 0,
|
|
32
|
-
interrupted: 1,
|
|
33
|
-
running: 2,
|
|
34
|
-
queued: 3,
|
|
35
|
-
completed: 4
|
|
36
|
-
};
|
|
37
|
-
const SORT_COMPARATORS = {
|
|
38
|
-
'depth-first': (a, b) => a.item.depth - b.item.depth || a.item.index - b.item.index,
|
|
39
|
-
'tools-desc': (a, b) => b.aggregate.totalTools - a.aggregate.totalTools,
|
|
40
|
-
'duration-desc': (a, b) => b.aggregate.totalDuration - a.aggregate.totalDuration,
|
|
41
|
-
status: (a, b) => STATUS_RANK[a.item.status] - STATUS_RANK[b.item.status]
|
|
42
|
-
};
|
|
43
|
-
const FILTER_PREDICATES = {
|
|
44
|
-
all: () => true,
|
|
45
|
-
leaf: n => n.children.length === 0,
|
|
46
|
-
running: n => n.item.status === 'running' || n.item.status === 'queued',
|
|
47
|
-
failed: n => n.item.status === 'failed' || n.item.status === 'interrupted'
|
|
48
|
-
};
|
|
49
|
-
const STATUS_GLYPH = {
|
|
50
|
-
running: { color: t => t.color.accent, glyph: '●' },
|
|
51
|
-
queued: { color: t => t.color.muted, glyph: '○' },
|
|
52
|
-
completed: { color: t => t.color.statusGood, glyph: '✓' },
|
|
53
|
-
interrupted: { color: t => t.color.warn, glyph: '■' },
|
|
54
|
-
failed: { color: t => t.color.error, glyph: '✗' }
|
|
55
|
-
};
|
|
56
|
-
// Heatmap palette — cold → hot, resolved against the active theme.
|
|
57
|
-
const heatPalette = (t) => [t.color.border, t.color.accent, t.color.primary, t.color.warn, t.color.error];
|
|
58
|
-
// ── Pure helpers ─────────────────────────────────────────────────────
|
|
59
|
-
const fmtDur = (seconds) => (seconds == null || seconds <= 0 ? '' : fmtDuration(seconds));
|
|
60
|
-
const fmtElapsedLabel = (seconds) => (seconds < 0 ? '' : fmtDuration(seconds));
|
|
61
|
-
const displayElapsedSeconds = (item, nowMs) => {
|
|
62
|
-
if (item.durationSeconds != null) {
|
|
63
|
-
return item.durationSeconds;
|
|
64
|
-
}
|
|
65
|
-
if (item.startedAt != null && (item.status === 'running' || item.status === 'queued')) {
|
|
66
|
-
return Math.max(0, (nowMs - item.startedAt) / 1000);
|
|
67
|
-
}
|
|
68
|
-
return null;
|
|
69
|
-
};
|
|
70
|
-
const indentFor = (depth) => ' '.repeat(Math.max(0, depth));
|
|
71
|
-
const formatRowId = (n) => String(n + 1).padStart(2, ' ');
|
|
72
|
-
const cycle = (order, current) => order[(order.indexOf(current) + 1) % order.length];
|
|
73
|
-
const statusGlyph = (item, t) => {
|
|
74
|
-
const g = STATUS_GLYPH[item.status];
|
|
75
|
-
return { color: g.color(t), glyph: g.glyph };
|
|
76
|
-
};
|
|
77
|
-
const prepareRows = (tree, sort, filter) => tree.length === 0 ? [] : flattenTree([...tree].sort(SORT_COMPARATORS[sort])).filter(FILTER_PREDICATES[filter]);
|
|
78
|
-
const diffMetricLine = (name, a, b, fmt) => {
|
|
79
|
-
const d = b - a;
|
|
80
|
-
const sign = d === 0 ? '' : d > 0 ? '+' : '-';
|
|
81
|
-
return `${name}: ${fmt(a)} → ${fmt(b)} (${sign}${fmt(Math.abs(d)) || '0'})`;
|
|
82
|
-
};
|
|
83
|
-
// ── Sub-components ───────────────────────────────────────────────────
|
|
84
|
-
/** Polled on parent `tick` so accordions can resize the thumb without a scroll event. */
|
|
85
|
-
function OverlayScrollbar({ scrollRef, t, tick }) {
|
|
86
|
-
void tick; // ensures re-render when the parent clock advances
|
|
87
|
-
const [hover, setHover] = useState(false);
|
|
88
|
-
const [grab, setGrab] = useState(null);
|
|
89
|
-
const s = scrollRef.current;
|
|
90
|
-
const vp = Math.max(0, s?.getViewportHeight() ?? 0);
|
|
91
|
-
if (!vp) {
|
|
92
|
-
return _jsx(Box, { width: 1 });
|
|
93
|
-
}
|
|
94
|
-
const total = Math.max(vp, s?.getScrollHeight() ?? vp);
|
|
95
|
-
const scrollable = total > vp;
|
|
96
|
-
const thumb = scrollable ? Math.max(1, Math.round((vp * vp) / total)) : vp;
|
|
97
|
-
const travel = Math.max(1, vp - thumb);
|
|
98
|
-
const pos = Math.max(0, (s?.getScrollTop() ?? 0) + (s?.getPendingDelta() ?? 0));
|
|
99
|
-
const thumbTop = scrollable ? Math.round((pos / Math.max(1, total - vp)) * travel) : 0;
|
|
100
|
-
const below = Math.max(0, vp - thumbTop - thumb);
|
|
101
|
-
const vBar = (n) => (n > 0 ? `${'│\n'.repeat(n - 1)}│` : '');
|
|
102
|
-
const thumbBody = `${'┃\n'.repeat(Math.max(0, thumb - 1))}┃`;
|
|
103
|
-
const thumbColor = grab !== null ? t.color.primary : t.color.accent;
|
|
104
|
-
const trackColor = hover ? t.color.border : t.color.muted;
|
|
105
|
-
const jump = (row, offset) => {
|
|
106
|
-
if (!s || !scrollable) {
|
|
107
|
-
return;
|
|
108
|
-
}
|
|
109
|
-
s.scrollTo(Math.round((Math.max(0, Math.min(travel, row - offset)) / travel) * Math.max(0, total - vp)));
|
|
110
|
-
};
|
|
111
|
-
return (_jsx(Box, { flexDirection: "column", onMouseDown: (e) => {
|
|
112
|
-
const row = Math.max(0, Math.min(vp - 1, e.localRow ?? 0));
|
|
113
|
-
const off = row >= thumbTop && row < thumbTop + thumb ? row - thumbTop : Math.floor(thumb / 2);
|
|
114
|
-
setGrab(off);
|
|
115
|
-
jump(row, off);
|
|
116
|
-
}, onMouseDrag: (e) => jump(Math.max(0, Math.min(vp - 1, e.localRow ?? 0)), grab ?? Math.floor(thumb / 2)), onMouseEnter: () => setHover(true), onMouseLeave: () => setHover(false), onMouseUp: () => setGrab(null), width: 1, children: !scrollable ? (_jsx(Text, { color: trackColor, dim: true, children: vBar(vp) })) : (_jsxs(_Fragment, { children: [thumbTop > 0 ? (_jsx(Text, { color: trackColor, dim: !hover, children: vBar(thumbTop) })) : null, _jsx(Text, { color: thumbColor, children: thumbBody }), below > 0 ? (_jsx(Text, { color: trackColor, dim: !hover, children: vBar(below) })) : null] })) }));
|
|
117
|
-
}
|
|
118
|
-
function GanttStrip({ cols, cursor, flatNodes, maxRows, now, t }) {
|
|
119
|
-
const spans = flatNodes
|
|
120
|
-
.map((node, idx) => {
|
|
121
|
-
const started = node.item.startedAt ?? now;
|
|
122
|
-
const ended = node.item.durationSeconds != null && node.item.startedAt != null
|
|
123
|
-
? node.item.startedAt + node.item.durationSeconds * 1000
|
|
124
|
-
: now;
|
|
125
|
-
return { endAt: ended, idx, node, startAt: started };
|
|
126
|
-
})
|
|
127
|
-
.filter(s => s.endAt >= s.startAt);
|
|
128
|
-
if (!spans.length) {
|
|
129
|
-
return null;
|
|
130
|
-
}
|
|
131
|
-
const globalStart = Math.min(...spans.map(s => s.startAt));
|
|
132
|
-
const globalEnd = Math.max(...spans.map(s => s.endAt));
|
|
133
|
-
const totalSpan = Math.max(1, globalEnd - globalStart);
|
|
134
|
-
const totalSeconds = (globalEnd - globalStart) / 1000;
|
|
135
|
-
// 5-col id gutter (" 12 ") so the bar doesn't press against the id.
|
|
136
|
-
// 10-col right reserve: pad + up to `12m 30s`-style label without
|
|
137
|
-
// truncate-end against a full-width bar.
|
|
138
|
-
const idGutter = 5;
|
|
139
|
-
const labelReserve = 10;
|
|
140
|
-
const barWidth = Math.max(10, cols - idGutter - labelReserve);
|
|
141
|
-
const startIdx = Math.max(0, Math.min(Math.max(0, spans.length - maxRows), cursor - Math.floor(maxRows / 2)));
|
|
142
|
-
const shown = spans.slice(startIdx, startIdx + maxRows);
|
|
143
|
-
const bar = (startAt, endAt) => {
|
|
144
|
-
const s = Math.floor(((startAt - globalStart) / totalSpan) * barWidth);
|
|
145
|
-
const e = Math.min(barWidth, Math.ceil(((endAt - globalStart) / totalSpan) * barWidth));
|
|
146
|
-
const fill = Math.max(1, e - s);
|
|
147
|
-
return ' '.repeat(s) + '█'.repeat(fill) + ' '.repeat(Math.max(0, barWidth - s - fill));
|
|
148
|
-
};
|
|
149
|
-
const charStep = totalSeconds < 20 && barWidth > 20 ? 5 : 10;
|
|
150
|
-
const ruler = Array.from({ length: barWidth }, (_, i) => {
|
|
151
|
-
if (i > 0 && i % 10 === 0) {
|
|
152
|
-
return '┼';
|
|
153
|
-
}
|
|
154
|
-
if (i > 0 && i % 5 === 0) {
|
|
155
|
-
return '·';
|
|
156
|
-
}
|
|
157
|
-
return '─';
|
|
158
|
-
}).join('');
|
|
159
|
-
const rulerLabels = (() => {
|
|
160
|
-
const chars = new Array(barWidth).fill(' ');
|
|
161
|
-
for (let pos = 0; pos < barWidth; pos += charStep) {
|
|
162
|
-
const secs = (pos / barWidth) * totalSeconds;
|
|
163
|
-
const label = pos === 0 ? '0' : secs >= 1 ? `${Math.round(secs)}s` : `${secs.toFixed(1)}s`;
|
|
164
|
-
for (let j = 0; j < label.length && pos + j < barWidth; j++) {
|
|
165
|
-
chars[pos + j] = label[j];
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
return chars.join('');
|
|
169
|
-
})();
|
|
170
|
-
const windowLabel = spans.length > maxRows ? ` (${startIdx + 1}-${Math.min(spans.length, startIdx + maxRows)}/${spans.length})` : '';
|
|
171
|
-
return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsxs(Text, { color: t.color.muted, children: ["Timeline \u00B7 ", fmtElapsedLabel(Math.max(0, totalSeconds)), windowLabel] }), shown.map(({ endAt, idx, node, startAt }) => {
|
|
172
|
-
const active = idx === cursor;
|
|
173
|
-
const { color } = statusGlyph(node.item, t);
|
|
174
|
-
const accent = active ? t.color.accent : t.color.muted;
|
|
175
|
-
const elSec = displayElapsedSeconds(node.item, now);
|
|
176
|
-
const elLabel = elSec != null ? fmtElapsedLabel(elSec) : '';
|
|
177
|
-
return (_jsxs(Text, { wrap: "truncate-end", children: [_jsxs(Text, { bold: active, color: accent, children: [formatRowId(idx), ' '] }), _jsx(Text, { color: active ? t.color.accent : color, children: bar(startAt, endAt) }), elLabel ? (_jsxs(Text, { color: accent, children: [' ', elLabel] })) : null] }, node.item.id));
|
|
178
|
-
}), _jsxs(Text, { color: t.color.muted, dim: true, children: [' ', ruler] }), totalSeconds > 0 ? (_jsxs(Text, { color: t.color.muted, dim: true, children: [' ', rulerLabels] })) : null] }));
|
|
179
|
-
}
|
|
180
|
-
function OverlaySection({ children, count, defaultOpen = false, title, t }) {
|
|
181
|
-
const openMap = useStore($overlaySectionsOpen);
|
|
182
|
-
const open = title in openMap ? openMap[title] : defaultOpen;
|
|
183
|
-
return (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Box, { onClick: () => toggleOverlaySection(title, defaultOpen), children: _jsxs(Text, { color: t.color.label, children: [_jsx(Text, { color: t.color.accent, children: open ? '▾ ' : '▸ ' }), title, typeof count === 'number' ? ` (${count})` : ''] }) }), open ? _jsx(Box, { flexDirection: "column", children: children }) : null] }));
|
|
184
|
-
}
|
|
185
|
-
function Field({ name, t, value }) {
|
|
186
|
-
return (_jsxs(Text, { wrap: "truncate-end", children: [_jsxs(Text, { color: t.color.label, children: [name, " \u00B7 "] }), _jsx(Text, { color: t.color.text, children: value })] }));
|
|
187
|
-
}
|
|
188
|
-
function Detail({ id, node, t }) {
|
|
189
|
-
const { aggregate: agg, item } = node;
|
|
190
|
-
const { color, glyph } = statusGlyph(item, t);
|
|
191
|
-
const inputTokens = item.inputTokens ?? 0;
|
|
192
|
-
const outputTokens = item.outputTokens ?? 0;
|
|
193
|
-
const localTokens = inputTokens + outputTokens;
|
|
194
|
-
const subtreeTokens = agg.inputTokens + agg.outputTokens - localTokens;
|
|
195
|
-
const localCost = item.costUsd ?? 0;
|
|
196
|
-
const subtreeCost = agg.costUsd - localCost;
|
|
197
|
-
const filesRead = item.filesRead ?? [];
|
|
198
|
-
const filesWritten = item.filesWritten ?? [];
|
|
199
|
-
const outputTail = item.outputTail ?? [];
|
|
200
|
-
// Tool calls: prefer the live stream; for archived / post-turn views
|
|
201
|
-
// that stream is often empty even when tool_count > 0, so fall back to
|
|
202
|
-
// the tool names captured in outputTail at subagent.complete time.
|
|
203
|
-
const toolLines = item.tools.length > 0 ? item.tools : outputTail.map(e => e.tool).filter(Boolean);
|
|
204
|
-
const filesOverflow = Math.max(0, filesRead.length - 8) + Math.max(0, filesWritten.length - 8);
|
|
205
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { bold: true, color: t.color.text, wrap: "wrap", children: [id ? _jsxs(Text, { color: t.color.accent, children: ["#", id, " "] }) : null, _jsx(Text, { color: color, children: glyph }), " ", item.goal] }), _jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Field, { name: "depth", t: t, value: `${item.depth} · ${item.status}` }), item.model ? _jsx(Field, { name: "model", t: t, value: item.model }) : null, item.toolsets?.length ? _jsx(Field, { name: "toolsets", t: t, value: item.toolsets.join(', ') }) : null, _jsx(Field, { name: "tools", t: t, value: `${item.toolCount ?? 0} (subtree ${agg.totalTools})` }), _jsx(Field, { name: "subtree", t: t, value: `${agg.descendantCount} agent${agg.descendantCount === 1 ? '' : 's'} · d${agg.maxDepthFromHere} · ⚡${agg.activeCount}` }), item.durationSeconds ? _jsx(Field, { name: "elapsed", t: t, value: fmtDur(item.durationSeconds) }) : null, item.iteration != null ? _jsx(Field, { name: "iteration", t: t, value: String(item.iteration) }) : null, item.apiCalls ? _jsx(Field, { name: "api calls", t: t, value: String(item.apiCalls) }) : null] }), localTokens > 0 || localCost > 0 ? (_jsxs(OverlaySection, { defaultOpen: true, t: t, title: "Budget", children: [localTokens > 0 ? (_jsx(Field, { name: "tokens", t: t, value: _jsxs(_Fragment, { children: [fmtTokens(inputTokens), " in \u00B7 ", fmtTokens(outputTokens), " out", item.reasoningTokens ? ` · ${fmtTokens(item.reasoningTokens)} reasoning` : ''] }) })) : null, localCost > 0 ? (_jsx(Field, { name: "cost", t: t, value: _jsxs(_Fragment, { children: [fmtCost(localCost), subtreeCost >= 0.01 ? ` · subtree +${fmtCost(subtreeCost)}` : ''] }) })) : null, subtreeTokens > 0 ? _jsx(Field, { name: "subtree tokens", t: t, value: `+${fmtTokens(subtreeTokens)}` }) : null] })) : null, filesRead.length > 0 || filesWritten.length > 0 ? (_jsxs(OverlaySection, { count: filesRead.length + filesWritten.length, t: t, title: "Files", children: [filesWritten.slice(0, 8).map((p, i) => (_jsxs(Text, { color: t.color.statusGood, wrap: "truncate-end", children: ["+", p] }, `w-${i}`))), filesRead.slice(0, 8).map((p, i) => (_jsxs(Text, { color: t.color.text, wrap: "truncate-end", children: [_jsx(Text, { color: t.color.muted, children: "\u00B7" }), " ", p] }, `r-${i}`))), filesOverflow > 0 ? _jsxs(Text, { color: t.color.muted, children: ["\u2026+", filesOverflow, " more"] }) : null] })) : null, toolLines.length > 0 ? (_jsx(OverlaySection, { count: toolLines.length, defaultOpen: true, t: t, title: "Tool calls", children: toolLines.map((line, i) => (_jsxs(Text, { color: t.color.text, wrap: "wrap", children: [_jsx(Text, { color: t.color.muted, children: "\u00B7" }), " ", line] }, i))) })) : null, outputTail.length > 0 ? (_jsx(OverlaySection, { count: outputTail.length, defaultOpen: true, t: t, title: "Output", children: outputTail.map((entry, i) => (_jsxs(Text, { color: entry.isError ? t.color.error : t.color.text, wrap: "wrap", children: [_jsx(Text, { bold: true, color: entry.isError ? t.color.error : t.color.accent, children: entry.tool }), ' ', entry.preview] }, i))) })) : null, item.notes.length ? (_jsx(OverlaySection, { count: item.notes.length, t: t, title: "Progress", children: item.notes.slice(-6).map((line, i) => (_jsxs(Text, { color: t.color.text, wrap: "wrap", children: [_jsx(Text, { color: t.color.label, children: "\u00B7" }), " ", line] }, i))) })) : null, item.summary ? (_jsx(OverlaySection, { defaultOpen: true, t: t, title: "Summary", children: _jsx(Text, { color: t.color.text, wrap: "wrap", children: item.summary }) })) : null] }));
|
|
206
|
-
}
|
|
207
|
-
function ListRow({ active, index, node, peak, t, width }) {
|
|
208
|
-
const { color, glyph } = statusGlyph(node.item, t);
|
|
209
|
-
const palette = heatPalette(t);
|
|
210
|
-
const heatIdx = hotnessBucket(node.aggregate.hotness, peak, palette.length);
|
|
211
|
-
const heatMarker = heatIdx >= 2 ? palette[heatIdx] : null;
|
|
212
|
-
const goal = compactPreview(node.item.goal || 'subagent', width - 28 - node.item.depth * 2);
|
|
213
|
-
const toolsCount = node.aggregate.totalTools > 0 ? ` ·${node.aggregate.totalTools}t` : '';
|
|
214
|
-
const kids = node.children.length ? ` ·${node.children.length}↓` : '';
|
|
215
|
-
const line = node.item.status === 'running' ? node.item.tools.at(-1) : undefined;
|
|
216
|
-
const paren = line ? line.indexOf('(') : -1;
|
|
217
|
-
const toolShort = line ? (paren > 0 ? line.slice(0, paren) : line).trim() : '';
|
|
218
|
-
const trailing = toolShort ? ` · ${compactPreview(toolShort, 14)}` : '';
|
|
219
|
-
const fg = active ? t.color.accent : t.color.text;
|
|
220
|
-
return (_jsxs(Text, { bold: active, color: fg, inverse: active, wrap: "truncate-end", children: [' ', _jsxs(Text, { color: active ? fg : t.color.muted, children: [formatRowId(index), " "] }), indentFor(node.item.depth), heatMarker ? _jsx(Text, { color: heatMarker, children: "\u258D" }) : null, _jsx(Text, { color: active ? fg : color, children: glyph }), " ", goal, _jsxs(Text, { color: active ? fg : t.color.muted, children: [toolsCount, kids, trailing] })] }));
|
|
221
|
-
}
|
|
222
|
-
function DiffPane({ label, snapshot, t, totals, width }) {
|
|
223
|
-
return (_jsxs(Box, { flexDirection: "column", width: width, children: [_jsx(Text, { bold: true, color: t.color.text, children: label }), _jsx(Text, { color: t.color.muted, wrap: "truncate-end", children: snapshot.label }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: t.color.muted, wrap: "truncate-end", children: formatSummary(totals) }) }), _jsx(Box, { flexDirection: "column", marginTop: 1, children: topLevelSubagents(snapshot.subagents)
|
|
224
|
-
.slice(0, 8)
|
|
225
|
-
.map(s => {
|
|
226
|
-
const { color, glyph } = statusGlyph(s, t);
|
|
227
|
-
return (_jsxs(Text, { color: t.color.muted, wrap: "truncate-end", children: [_jsx(Text, { color: color, children: glyph }), " ", s.goal || 'subagent'] }, s.id));
|
|
228
|
-
}) })] }));
|
|
229
|
-
}
|
|
230
|
-
function DiffView({ cols, onClose, pair, t }) {
|
|
231
|
-
const aTotals = useMemo(() => treeTotals(buildSubagentTree(pair.baseline.subagents)), [pair.baseline]);
|
|
232
|
-
const bTotals = useMemo(() => treeTotals(buildSubagentTree(pair.candidate.subagents)), [pair.candidate]);
|
|
233
|
-
const paneWidth = Math.floor((cols - 4) / 2);
|
|
234
|
-
useInput((ch, key) => {
|
|
235
|
-
if (key.escape || ch === 'q') {
|
|
236
|
-
onClose();
|
|
237
|
-
}
|
|
238
|
-
});
|
|
239
|
-
const round = (n) => String(Math.round(n));
|
|
240
|
-
const sumTokens = (x) => x.inputTokens + x.outputTokens;
|
|
241
|
-
const dollars = (n) => fmtCost(n) || '$0.00';
|
|
242
|
-
return (_jsxs(Box, { flexDirection: "column", flexGrow: 1, paddingX: 1, paddingY: 1, children: [_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsx(Text, { bold: true, color: t.color.border, children: "Replay diff" }), _jsx(Text, { color: t.color.muted, children: "baseline vs candidate \u00B7 esc/q close" })] }), _jsxs(Box, { flexDirection: "row", marginBottom: 1, children: [_jsx(DiffPane, { label: "A \u00B7 baseline", snapshot: pair.baseline, t: t, totals: aTotals, width: paneWidth }), _jsx(Box, { width: 2 }), _jsx(DiffPane, { label: "B \u00B7 candidate", snapshot: pair.candidate, t: t, totals: bTotals, width: paneWidth })] }), _jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { bold: true, color: t.color.accent, children: "\u0394" }), _jsx(Text, { color: t.color.text, children: diffMetricLine('agents', aTotals.descendantCount, bTotals.descendantCount, round) }), _jsx(Text, { color: t.color.text, children: diffMetricLine('tools', aTotals.totalTools, bTotals.totalTools, round) }), _jsx(Text, { color: t.color.text, children: diffMetricLine('depth', aTotals.maxDepthFromHere, bTotals.maxDepthFromHere, round) }), _jsx(Text, { color: t.color.text, children: diffMetricLine('duration', aTotals.totalDuration, bTotals.totalDuration, n => `${n.toFixed(1)}s`) }), _jsx(Text, { color: t.color.text, children: diffMetricLine('tokens', sumTokens(aTotals), sumTokens(bTotals), fmtTokens) }), _jsx(Text, { color: t.color.text, children: diffMetricLine('cost', aTotals.costUsd, bTotals.costUsd, dollars) })] })] }));
|
|
243
|
-
}
|
|
244
|
-
// ── Main overlay ─────────────────────────────────────────────────────
|
|
245
|
-
export function AgentsOverlay({ gw, initialHistoryIndex = 0, onClose, t }) {
|
|
246
|
-
const liveSubagents = useTurnSelector(state => state.subagents);
|
|
247
|
-
const delegation = useStore($delegationState);
|
|
248
|
-
const history = useStore($spawnHistory);
|
|
249
|
-
const diffPair = useStore($spawnDiff);
|
|
250
|
-
const { stdout } = useStdout();
|
|
251
|
-
// historyIndex === 0: live turn. 1..N pulls the Nth-most-recent archived
|
|
252
|
-
// snapshot. /replay passes N on open.
|
|
253
|
-
const [historyIndex, setHistoryIndex] = useState(() => Math.max(0, Math.min(history.length, Math.floor(initialHistoryIndex))));
|
|
254
|
-
const [sort, setSort] = useState('depth-first');
|
|
255
|
-
const [filter, setFilter] = useState('all');
|
|
256
|
-
const [cursor, setCursor] = useState(0);
|
|
257
|
-
const [flash, setFlash] = useState('');
|
|
258
|
-
const [now, setNow] = useState(() => Date.now());
|
|
259
|
-
// cc-style view switching: list = full-width row picker, detail = full-width
|
|
260
|
-
// scrollable pane. Two panes side-by-side in Ink fought Yoga flex.
|
|
261
|
-
const [mode, setMode] = useState('list');
|
|
262
|
-
const detailScrollRef = useRef(null);
|
|
263
|
-
const prevLiveCountRef = useRef(liveSubagents.length);
|
|
264
|
-
// ── Derived state ──────────────────────────────────────────────────
|
|
265
|
-
const activeSnapshot = historyIndex > 0 ? history[historyIndex - 1] : null;
|
|
266
|
-
// Instant fallback to history[0] the moment the live list clears — avoids
|
|
267
|
-
// a one-frame "no subagents" flash while the auto-follow effect fires.
|
|
268
|
-
const justFinishedSnapshot = historyIndex === 0 && liveSubagents.length === 0 ? (history[0] ?? null) : null;
|
|
269
|
-
const effectiveSnapshot = activeSnapshot ?? justFinishedSnapshot;
|
|
270
|
-
const replayMode = effectiveSnapshot != null;
|
|
271
|
-
const subagents = replayMode ? effectiveSnapshot.subagents : liveSubagents;
|
|
272
|
-
const tree = useMemo(() => buildSubagentTree(subagents), [subagents]);
|
|
273
|
-
const totals = useMemo(() => treeTotals(tree), [tree]);
|
|
274
|
-
const widths = useMemo(() => widthByDepth(tree), [tree]);
|
|
275
|
-
const spark = useMemo(() => sparkline(widths), [widths]);
|
|
276
|
-
const peak = useMemo(() => peakHotness(tree), [tree]);
|
|
277
|
-
const rows = useMemo(() => prepareRows(tree, sort, filter), [tree, sort, filter]);
|
|
278
|
-
const selected = rows[cursor] ?? null;
|
|
279
|
-
const cols = stdout?.columns ?? 80;
|
|
280
|
-
const rowsH = Math.max(8, (stdout?.rows ?? 24) - 10);
|
|
281
|
-
const listWindowStart = Math.max(0, cursor - Math.floor(rowsH / 2));
|
|
282
|
-
// ── Effects ────────────────────────────────────────────────────────
|
|
283
|
-
useEffect(() => {
|
|
284
|
-
// Ticker drives both the live gantt and OverlayScrollbar content-reflow
|
|
285
|
-
// detection. Slower in replay (nothing's growing) but not stopped
|
|
286
|
-
// because accordions still expand.
|
|
287
|
-
const id = setInterval(() => setNow(Date.now()), replayMode ? 300 : 500);
|
|
288
|
-
return () => clearInterval(id);
|
|
289
|
-
}, [replayMode]);
|
|
290
|
-
useEffect(() => {
|
|
291
|
-
// Clamp stale index when history grows/shrinks beneath us.
|
|
292
|
-
if (historyIndex > history.length) {
|
|
293
|
-
setHistoryIndex(history.length);
|
|
294
|
-
}
|
|
295
|
-
}, [history.length, historyIndex]);
|
|
296
|
-
useEffect(() => {
|
|
297
|
-
// Auto-follow the just-finished turn onto history[1] so the user isn't
|
|
298
|
-
// dropped into an empty live view. Fires only when transitioning from
|
|
299
|
-
// "had live subagents" → "live empty" while in live mode.
|
|
300
|
-
const prev = prevLiveCountRef.current;
|
|
301
|
-
prevLiveCountRef.current = liveSubagents.length;
|
|
302
|
-
if (historyIndex === 0 && prev > 0 && liveSubagents.length === 0 && history.length > 0) {
|
|
303
|
-
setHistoryIndex(1);
|
|
304
|
-
setCursor(0);
|
|
305
|
-
setFlash('turn finished · inspect freely · q to close');
|
|
306
|
-
}
|
|
307
|
-
}, [history.length, historyIndex, liveSubagents.length]);
|
|
308
|
-
useEffect(() => {
|
|
309
|
-
// Reset detail scroll on navigation so the top of the new node shows.
|
|
310
|
-
detailScrollRef.current?.scrollTo(0);
|
|
311
|
-
}, [cursor, historyIndex, mode]);
|
|
312
|
-
useEffect(() => {
|
|
313
|
-
// Warm caps + paused flag on open.
|
|
314
|
-
gw.request('delegation.status', {})
|
|
315
|
-
.then(r => applyDelegationStatus(asRpcResult(r)))
|
|
316
|
-
.catch(() => { });
|
|
317
|
-
}, [gw]);
|
|
318
|
-
useEffect(() => {
|
|
319
|
-
if (cursor >= rows.length) {
|
|
320
|
-
setCursor(Math.max(0, rows.length - 1));
|
|
321
|
-
}
|
|
322
|
-
}, [cursor, rows.length]);
|
|
323
|
-
// ── Actions ────────────────────────────────────────────────────────
|
|
324
|
-
const guardLive = (action) => {
|
|
325
|
-
if (replayMode) {
|
|
326
|
-
setFlash('replay mode — controls disabled');
|
|
327
|
-
}
|
|
328
|
-
else {
|
|
329
|
-
action();
|
|
330
|
-
}
|
|
331
|
-
};
|
|
332
|
-
const interrupt = (id) => gw.request('subagent.interrupt', { subagent_id: id });
|
|
333
|
-
const killOne = (id) => guardLive(() => {
|
|
334
|
-
interrupt(id)
|
|
335
|
-
.then(raw => {
|
|
336
|
-
const r = asRpcResult(raw);
|
|
337
|
-
setFlash(r?.found ? `killing ${id}` : `not found: ${id}`);
|
|
338
|
-
})
|
|
339
|
-
.catch(() => setFlash(`kill failed: ${id}`));
|
|
340
|
-
});
|
|
341
|
-
const killSubtree = (node) => guardLive(() => {
|
|
342
|
-
const ids = [node.item.id, ...descendantIds(node)];
|
|
343
|
-
ids.forEach(id => interrupt(id).catch(() => { }));
|
|
344
|
-
setFlash(`killing subtree · ${ids.length} node${ids.length === 1 ? '' : 's'}`);
|
|
345
|
-
});
|
|
346
|
-
const togglePause = () => guardLive(() => {
|
|
347
|
-
gw.request('delegation.pause', { paused: !delegation.paused })
|
|
348
|
-
.then(raw => {
|
|
349
|
-
const r = asRpcResult(raw);
|
|
350
|
-
applyDelegationStatus({ paused: r?.paused });
|
|
351
|
-
setFlash(r?.paused ? 'spawning paused' : 'spawning resumed');
|
|
352
|
-
})
|
|
353
|
-
.catch(() => setFlash('pause failed'));
|
|
354
|
-
});
|
|
355
|
-
const stepHistory = (delta) => setHistoryIndex(idx => {
|
|
356
|
-
const next = Math.max(0, Math.min(history.length, idx + delta));
|
|
357
|
-
if (next !== idx) {
|
|
358
|
-
setCursor(0);
|
|
359
|
-
setFlash(next === 0 ? 'live turn' : `replay · ${next}/${history.length}`);
|
|
360
|
-
}
|
|
361
|
-
return next;
|
|
362
|
-
});
|
|
363
|
-
const closeWithCleanup = () => {
|
|
364
|
-
clearDiffPair();
|
|
365
|
-
onClose();
|
|
366
|
-
};
|
|
367
|
-
// ── Input ──────────────────────────────────────────────────────────
|
|
368
|
-
const detailPageSize = Math.max(4, rowsH - 2);
|
|
369
|
-
const wheelDetailDy = 3;
|
|
370
|
-
const scrollDetail = (dy) => detailScrollRef.current?.scrollBy(dy);
|
|
371
|
-
useInput((ch, key) => {
|
|
372
|
-
if (ch === 'q') {
|
|
373
|
-
return closeWithCleanup();
|
|
374
|
-
}
|
|
375
|
-
if (key.escape) {
|
|
376
|
-
return mode === 'detail' ? setMode('list') : closeWithCleanup();
|
|
377
|
-
}
|
|
378
|
-
// Shared actions (both modes).
|
|
379
|
-
if (ch === '<' || ch === '[') {
|
|
380
|
-
return stepHistory(1);
|
|
381
|
-
}
|
|
382
|
-
if (ch === '>' || ch === ']') {
|
|
383
|
-
return stepHistory(-1);
|
|
384
|
-
}
|
|
385
|
-
if (ch === 'p') {
|
|
386
|
-
return togglePause();
|
|
387
|
-
}
|
|
388
|
-
if (ch === 'x' && selected) {
|
|
389
|
-
return killOne(selected.item.id);
|
|
390
|
-
}
|
|
391
|
-
if (ch === 'X' && selected) {
|
|
392
|
-
return killSubtree(selected);
|
|
393
|
-
}
|
|
394
|
-
if (mode === 'detail') {
|
|
395
|
-
if (key.leftArrow || ch === 'h') {
|
|
396
|
-
return setMode('list');
|
|
397
|
-
}
|
|
398
|
-
if (key.pageUp || (key.ctrl && ch === 'u')) {
|
|
399
|
-
return scrollDetail(-detailPageSize);
|
|
400
|
-
}
|
|
401
|
-
if (key.pageDown || (key.ctrl && ch === 'd')) {
|
|
402
|
-
return scrollDetail(detailPageSize);
|
|
403
|
-
}
|
|
404
|
-
if (key.wheelUp) {
|
|
405
|
-
return scrollDetail(-wheelDetailDy);
|
|
406
|
-
}
|
|
407
|
-
if (key.wheelDown) {
|
|
408
|
-
return scrollDetail(wheelDetailDy);
|
|
409
|
-
}
|
|
410
|
-
if (key.upArrow || ch === 'k') {
|
|
411
|
-
return scrollDetail(-2);
|
|
412
|
-
}
|
|
413
|
-
if (key.downArrow || ch === 'j') {
|
|
414
|
-
return scrollDetail(2);
|
|
415
|
-
}
|
|
416
|
-
if (ch === 'g') {
|
|
417
|
-
return detailScrollRef.current?.scrollTo(0);
|
|
418
|
-
}
|
|
419
|
-
if (ch === 'G') {
|
|
420
|
-
return detailScrollRef.current?.scrollToBottom?.();
|
|
421
|
-
}
|
|
422
|
-
return;
|
|
423
|
-
}
|
|
424
|
-
// List mode.
|
|
425
|
-
if ((key.return || key.rightArrow || ch === 'l') && selected) {
|
|
426
|
-
return setMode('detail');
|
|
427
|
-
}
|
|
428
|
-
if (key.upArrow || ch === 'k' || key.wheelUp) {
|
|
429
|
-
return setCursor(c => Math.max(0, c - 1));
|
|
430
|
-
}
|
|
431
|
-
if (key.downArrow || ch === 'j' || key.wheelDown) {
|
|
432
|
-
return setCursor(c => Math.min(Math.max(0, rows.length - 1), c + 1));
|
|
433
|
-
}
|
|
434
|
-
if (ch === 'g') {
|
|
435
|
-
return setCursor(0);
|
|
436
|
-
}
|
|
437
|
-
if (ch === 'G') {
|
|
438
|
-
return setCursor(Math.max(0, rows.length - 1));
|
|
439
|
-
}
|
|
440
|
-
if (ch === 's') {
|
|
441
|
-
return setSort(m => cycle(SORT_ORDER, m));
|
|
442
|
-
}
|
|
443
|
-
if (ch === 'f') {
|
|
444
|
-
return setFilter(m => cycle(FILTER_ORDER, m));
|
|
445
|
-
}
|
|
446
|
-
});
|
|
447
|
-
// ── Header assembly ────────────────────────────────────────────────
|
|
448
|
-
const mix = Object.entries(subagents.reduce((acc, it) => {
|
|
449
|
-
const key = it.model ? it.model.split('/').pop() : 'inherit';
|
|
450
|
-
acc[key] = (acc[key] ?? 0) + 1;
|
|
451
|
-
return acc;
|
|
452
|
-
}, {}))
|
|
453
|
-
.sort((a, b) => b[1] - a[1])
|
|
454
|
-
.slice(0, 4)
|
|
455
|
-
.map(([k, v]) => `${k}×${v}`)
|
|
456
|
-
.join(' · ');
|
|
457
|
-
const capsLabel = delegation.maxSpawnDepth
|
|
458
|
-
? `caps d${delegation.maxSpawnDepth}/${delegation.maxConcurrentChildren ?? '?'}`
|
|
459
|
-
: '';
|
|
460
|
-
const title = replayMode && effectiveSnapshot
|
|
461
|
-
? `${historyIndex > 0 ? `Replay ${historyIndex}/${history.length}` : 'Last turn'} · finished ${new Date(effectiveSnapshot.finishedAt).toLocaleTimeString()}`
|
|
462
|
-
: `Spawn tree${delegation.paused ? ' · ⏸ paused' : ''}`;
|
|
463
|
-
const metaLine = [formatSummary(totals), spark, capsLabel, mix ? `· ${mix}` : ''].filter(Boolean).join(' ');
|
|
464
|
-
const controlsHint = replayMode
|
|
465
|
-
? ' · controls locked'
|
|
466
|
-
: ` · x kill · X subtree · p ${delegation.paused ? 'resume' : 'pause'}`;
|
|
467
|
-
// ── Rendering ──────────────────────────────────────────────────────
|
|
468
|
-
if (diffPair) {
|
|
469
|
-
return _jsx(DiffView, { cols: cols, onClose: closeWithCleanup, pair: diffPair, t: t });
|
|
470
|
-
}
|
|
471
|
-
return (_jsxs(Box, { alignItems: "stretch", flexDirection: "column", flexGrow: 1, paddingX: 1, paddingY: 1, children: [_jsx(Box, { flexDirection: "column", marginBottom: 1, children: _jsxs(Text, { wrap: "truncate-end", children: [_jsx(Text, { bold: true, color: replayMode ? t.color.border : t.color.primary, children: title }), metaLine ? (_jsxs(Text, { color: t.color.muted, children: [' ', metaLine] })) : null] }) }), rows.length === 0 ? (_jsx(Box, { flexDirection: "column", flexGrow: 1, children: _jsx(Text, { color: t.color.muted, children: "No subagents this turn. Trigger delegate_task to populate the tree." }) })) : mode === 'list' ? (_jsxs(Box, { flexDirection: "column", flexGrow: 1, flexShrink: 1, minHeight: 0, children: [_jsx(GanttStrip, { cols: cols, cursor: cursor, flatNodes: rows, maxRows: 6, now: now, t: t }), _jsx(Box, { flexDirection: "column", flexGrow: 0, flexShrink: 0, overflow: "hidden", children: rows.slice(listWindowStart, listWindowStart + rowsH).map((node, i) => (_jsx(ListRow, { active: listWindowStart + i === cursor, index: listWindowStart + i, node: node, peak: peak, t: t, width: cols }, node.item.id))) })] })) : (_jsxs(Box, { flexDirection: "row", flexGrow: 1, flexShrink: 1, minHeight: 0, children: [_jsx(ScrollBox, { flexDirection: "column", flexGrow: 1, flexShrink: 1, ref: detailScrollRef, children: _jsx(Box, { flexDirection: "column", paddingBottom: 4, paddingRight: 1, children: selected ? _jsx(Detail, { id: formatRowId(cursor).trim(), node: selected, t: t }) : null }) }), _jsx(NoSelect, { flexShrink: 0, marginLeft: 1, children: _jsx(OverlayScrollbar, { scrollRef: detailScrollRef, t: t, tick: now }) })] })), _jsxs(Box, { flexDirection: "column", marginTop: 1, children: [flash ? _jsx(Text, { color: t.color.accent, children: flash }) : null, mode === 'list' ? (_jsxs(Text, { color: t.color.muted, children: ["\u2191\u2193/jk move \u00B7 g/G top/bottom \u00B7 Enter/\u2192 open detail", controlsHint, " \u00B7 s sort:", SORT_LABEL[sort], " \u00B7 f filter:", FILTER_LABEL[filter], history.length > 0 ? ` · [ / ] history ${historyIndex}/${history.length}` : '', ' · q close'] })) : (_jsxs(Text, { color: t.color.muted, children: ["\u2191\u2193/jk scroll \u00B7 PgUp/PgDn page \u00B7 g/G top/bottom \u00B7 Esc/\u2190 back to list", controlsHint, " \u00B7 q close"] }))] })] }));
|
|
472
|
-
}
|
|
473
|
-
export const closeAgentsOverlay = () => patchOverlayState({ agents: false });
|
|
474
|
-
export const openAgentsOverlay = () => patchOverlayState({ agents: true });
|