@pellux/goodvibes-tui 1.1.0 → 1.7.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/CHANGELOG.md +85 -21
- package/README.md +20 -11
- package/docs/foundation-artifacts/operator-contract.json +1 -1
- package/package.json +2 -2
- package/src/core/alert-gating.ts +67 -0
- package/src/core/approval-alert.ts +72 -0
- package/src/core/budget-breach-notifier.ts +106 -0
- package/src/core/conversation-rendering.ts +19 -0
- package/src/core/conversation.ts +18 -0
- package/src/core/focus-tracker.ts +41 -0
- package/src/core/long-task-notifier.ts +33 -6
- package/src/core/system-message-router.ts +37 -51
- package/src/core/turn-cancellation.ts +20 -0
- package/src/core/turn-event-wiring.ts +64 -3
- package/src/export/cost-utils.ts +36 -0
- package/src/input/command-registry.ts +38 -1
- package/src/input/commands/checkpoint-runtime.ts +280 -0
- package/src/input/commands/codebase-runtime.ts +192 -0
- package/src/input/commands/eval.ts +1 -1
- package/src/input/commands/health-runtime.ts +1 -1
- package/src/input/commands/image-runtime.ts +112 -0
- package/src/input/commands/intelligence-runtime.ts +11 -1
- package/src/input/commands/local-auth-runtime.ts +4 -1
- package/src/input/commands/local-runtime.ts +9 -3
- package/src/input/commands/marketplace-runtime.ts +2 -2
- package/src/input/commands/memory.ts +6 -1
- package/src/input/commands/operator-panel-runtime.ts +40 -24
- package/src/input/commands/planning-runtime.ts +26 -3
- package/src/input/commands/platform-sandbox-runtime.ts +1 -6
- package/src/input/commands/plugin-runtime.ts +2 -2
- package/src/input/commands/policy-dispatch.ts +21 -0
- package/src/input/commands/provider-accounts-runtime.ts +1 -1
- package/src/input/commands/qrcode-runtime.ts +3 -3
- package/src/input/commands/recall-review.ts +35 -0
- package/src/input/commands/remote-runtime.ts +3 -3
- package/src/input/commands/runtime-services.ts +54 -13
- package/src/input/commands/services-runtime.ts +1 -1
- package/src/input/commands/session-workflow.ts +16 -1
- package/src/input/commands/settings-sync-runtime.ts +1 -1
- package/src/input/commands/shell-core.ts +15 -7
- package/src/input/commands/skills-runtime.ts +2 -8
- package/src/input/commands/subscription-runtime.ts +2 -2
- package/src/input/commands/test-runtime.ts +277 -0
- package/src/input/commands/websearch-runtime.ts +101 -0
- package/src/input/commands/work-plan-runtime.ts +36 -10
- package/src/input/commands/workstream-runtime.ts +338 -0
- package/src/input/commands.ts +12 -0
- package/src/input/config-modal-types.ts +161 -0
- package/src/input/config-modal.ts +377 -0
- package/src/input/feed-context-factory.ts +7 -8
- package/src/input/handler-command-route.ts +27 -17
- package/src/input/handler-feed-routes.ts +61 -23
- package/src/input/handler-feed.ts +47 -23
- package/src/input/handler-interactions.ts +1 -3
- package/src/input/handler-modal-routes.ts +65 -0
- package/src/input/handler-modal-stack.ts +3 -5
- package/src/input/handler-modal-token-routes.ts +16 -49
- package/src/input/handler-picker-routes.ts +11 -94
- package/src/input/handler-shortcuts.ts +24 -14
- package/src/input/handler-types.ts +2 -6
- package/src/input/handler-ui-state.ts +10 -22
- package/src/input/handler.ts +6 -28
- package/src/input/keybindings.ts +22 -8
- package/src/input/model-picker-types.ts +24 -6
- package/src/input/model-picker.ts +58 -0
- package/src/input/panel-integration-actions.ts +9 -170
- package/src/input/settings-modal-data.ts +95 -0
- package/src/input/settings-modal-mutations.ts +6 -4
- package/src/main.ts +24 -27
- package/src/panels/agent-inspector-shared.ts +2 -1
- package/src/panels/base-panel.ts +22 -1
- package/src/panels/builtin/agent.ts +21 -107
- package/src/panels/builtin/development.ts +15 -61
- package/src/panels/builtin/knowledge.ts +8 -32
- package/src/panels/builtin/operations.ts +84 -428
- package/src/panels/builtin/session.ts +21 -112
- package/src/panels/builtin/shared.ts +9 -43
- package/src/panels/builtin-modals.ts +218 -0
- package/src/panels/builtin-panels.ts +5 -0
- package/src/panels/cost-tracker-panel.ts +36 -10
- package/src/panels/diff-panel.ts +12 -43
- package/src/panels/eval-registry.ts +60 -0
- package/src/panels/fleet-panel.ts +800 -0
- package/src/panels/fleet-read-model.ts +477 -0
- package/src/panels/fleet-steer.ts +92 -0
- package/src/panels/fleet-stop.ts +125 -0
- package/src/panels/fleet-tabs.ts +230 -0
- package/src/panels/fleet-transcript.ts +356 -0
- package/src/panels/index.ts +7 -31
- package/src/panels/modals/hooks-modal.ts +187 -0
- package/src/panels/modals/keybindings-modal.ts +151 -0
- package/src/panels/modals/knowledge-modal.ts +158 -0
- package/src/panels/modals/local-auth-modal.ts +132 -0
- package/src/panels/modals/marketplace-modal.ts +218 -0
- package/src/panels/modals/memory-modal.ts +180 -0
- package/src/panels/modals/modal-surface-helpers.ts +54 -0
- package/src/panels/modals/modal-theme.ts +36 -0
- package/src/panels/modals/pairing-modal.ts +144 -0
- package/src/panels/modals/planning-modal.ts +282 -0
- package/src/panels/modals/plugins-modal.ts +174 -0
- package/src/panels/modals/policy-modal.ts +256 -0
- package/src/panels/modals/provider-health-modal.ts +136 -0
- package/src/panels/modals/remote-modal.ts +115 -0
- package/src/panels/modals/sandbox-modal.ts +150 -0
- package/src/panels/modals/security-modal.ts +217 -0
- package/src/panels/modals/services-modal.ts +179 -0
- package/src/panels/modals/settings-sync-modal.ts +142 -0
- package/src/panels/modals/skills-modal.ts +189 -0
- package/src/panels/modals/subscription-modal.ts +172 -0
- package/src/panels/modals/work-plan-modal.ts +149 -0
- package/src/panels/panel-confirm-overlay.ts +72 -0
- package/src/panels/panel-manager.ts +108 -5
- package/src/panels/project-planning-answer-actions.ts +4 -2
- package/src/panels/skills-panel.ts +7 -51
- package/src/panels/types.ts +13 -0
- package/src/permissions/hunk-selection.ts +179 -0
- package/src/permissions/prompt.ts +60 -4
- package/src/renderer/compaction-history-modal.ts +19 -3
- package/src/renderer/compaction-preview.ts +13 -2
- package/src/renderer/compaction-quality.ts +100 -0
- package/src/renderer/config-modal.ts +81 -0
- package/src/renderer/conversation-overlays.ts +10 -17
- package/src/renderer/fleet-tab-strip.ts +56 -0
- package/src/renderer/footer-tips.ts +10 -2
- package/src/renderer/git-status.ts +40 -0
- package/src/renderer/help-overlay.ts +2 -2
- package/src/renderer/model-workspace.ts +44 -1
- package/src/renderer/panel-workspace-bar.ts +8 -2
- package/src/renderer/turn-injection.ts +114 -0
- package/src/runtime/bootstrap-command-context.ts +21 -1
- package/src/runtime/bootstrap-command-parts.ts +34 -7
- package/src/runtime/bootstrap-core.ts +14 -4
- package/src/runtime/bootstrap-shell.ts +29 -16
- package/src/runtime/bootstrap.ts +11 -17
- package/src/runtime/code-index-services.ts +112 -0
- package/src/runtime/orchestrator-core-services.ts +49 -0
- package/src/runtime/process-lifecycle.ts +3 -1
- package/src/runtime/services.ts +91 -43
- package/src/runtime/ui-services.ts +8 -0
- package/src/runtime/workstream-services.ts +195 -0
- package/src/shell/blocking-input.ts +22 -1
- package/src/shell/ui-openers.ts +129 -17
- package/src/utils/format-duration.ts +8 -18
- package/src/utils/splash-lines.ts +1 -1
- package/src/version.ts +1 -1
- package/src/panels/agent-inspector-panel.ts +0 -786
- package/src/panels/approval-panel.ts +0 -252
- package/src/panels/automation-control-panel.ts +0 -479
- package/src/panels/cockpit-panel.ts +0 -481
- package/src/panels/cockpit-read-model.ts +0 -238
- package/src/panels/communication-panel.ts +0 -256
- package/src/panels/control-plane-panel.ts +0 -470
- package/src/panels/debug-panel.ts +0 -615
- package/src/panels/docs-panel.ts +0 -384
- package/src/panels/eval-panel.ts +0 -627
- package/src/panels/file-explorer-panel.ts +0 -673
- package/src/panels/file-preview-panel.ts +0 -517
- package/src/panels/hooks-panel.ts +0 -401
- package/src/panels/incident-review-panel.ts +0 -406
- package/src/panels/intelligence-panel.ts +0 -383
- package/src/panels/knowledge-graph-panel.ts +0 -515
- package/src/panels/marketplace-panel.ts +0 -399
- package/src/panels/memory-panel.ts +0 -558
- package/src/panels/ops-control-panel.ts +0 -329
- package/src/panels/ops-strategy-panel.ts +0 -321
- package/src/panels/orchestration-panel.ts +0 -465
- package/src/panels/panel-list-panel.ts +0 -566
- package/src/panels/plan-dashboard-panel.ts +0 -707
- package/src/panels/policy-panel.ts +0 -517
- package/src/panels/project-planning-panel.ts +0 -731
- package/src/panels/provider-health-panel.ts +0 -678
- package/src/panels/provider-health-tracker.ts +0 -310
- package/src/panels/provider-health-views.ts +0 -567
- package/src/panels/qr-panel.ts +0 -280
- package/src/panels/remote-panel.ts +0 -534
- package/src/panels/routes-panel.ts +0 -241
- package/src/panels/sandbox-panel.ts +0 -456
- package/src/panels/security-panel.ts +0 -447
- package/src/panels/services-panel.ts +0 -329
- package/src/panels/session-browser-panel.ts +0 -496
- package/src/panels/settings-sync-panel.ts +0 -398
- package/src/panels/subscription-panel.ts +0 -342
- package/src/panels/symbol-outline-panel.ts +0 -619
- package/src/panels/system-messages-panel.ts +0 -364
- package/src/panels/tasks-panel.ts +0 -608
- package/src/panels/thinking-panel.ts +0 -333
- package/src/panels/tool-inspector-panel.ts +0 -537
- package/src/panels/work-plan-panel.ts +0 -540
- package/src/panels/worktree-panel.ts +0 -360
- package/src/panels/wrfc-panel.ts +0 -790
- package/src/renderer/agent-detail-modal.ts +0 -466
- package/src/renderer/live-tail-modal.ts +0 -156
- package/src/renderer/process-modal.ts +0 -671
- package/src/renderer/qr-renderer.ts +0 -120
|
@@ -1,333 +0,0 @@
|
|
|
1
|
-
// ---------------------------------------------------------------------------
|
|
2
|
-
// ThinkingPanel — streams model reasoning tokens in real-time.
|
|
3
|
-
// ---------------------------------------------------------------------------
|
|
4
|
-
|
|
5
|
-
import type { Line } from '../types/grid.ts';
|
|
6
|
-
import { BasePanel } from './base-panel.ts';
|
|
7
|
-
import type { TurnEvent } from '@/runtime/index.ts';
|
|
8
|
-
import type { UiEventFeed } from '../runtime/ui-events.ts';
|
|
9
|
-
import {
|
|
10
|
-
buildEmptyState,
|
|
11
|
-
buildPanelLine,
|
|
12
|
-
buildStyledPanelLine,
|
|
13
|
-
buildPanelWorkspace,
|
|
14
|
-
resolveScrollablePanelSection,
|
|
15
|
-
extendPalette,
|
|
16
|
-
DEFAULT_PANEL_PALETTE,
|
|
17
|
-
type PanelWorkspaceSection,
|
|
18
|
-
} from './polish.ts';
|
|
19
|
-
import { truncateDisplay, wrapText } from '../utils/terminal-width.ts';
|
|
20
|
-
|
|
21
|
-
const C = extendPalette(DEFAULT_PANEL_PALETTE, {
|
|
22
|
-
// Domain-specific reasoning tones (purples) and the active-stream cyan have no
|
|
23
|
-
// clean shared equivalent; selection background maps to the shared selectBg.
|
|
24
|
-
reasoningFg: '#aa88ff',
|
|
25
|
-
activeFg: '#cc99ff',
|
|
26
|
-
turnLabel: '#7766bb',
|
|
27
|
-
activeLabel: '#00ffff',
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
interface ReasoningBlock {
|
|
31
|
-
// Real turn id from the TURN_*/STREAM_* event envelope — joins this block
|
|
32
|
-
// to the same turn's rows in TasksPanel and the tool inspector, which key
|
|
33
|
-
// off the identical turnId string.
|
|
34
|
-
turnId: string;
|
|
35
|
-
// Distributed-tracing correlation id (EventEnvelope.traceId), when the
|
|
36
|
-
// originating event carried one — same field RuntimeTask.correlationId
|
|
37
|
-
// joins against.
|
|
38
|
-
correlationId?: string;
|
|
39
|
-
content: string;
|
|
40
|
-
active: boolean; // true = currently streaming
|
|
41
|
-
collapsed: boolean;
|
|
42
|
-
startedAt: number;
|
|
43
|
-
updatedAt: number;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function fmtClock(ts: number): string {
|
|
47
|
-
const d = new Date(ts);
|
|
48
|
-
const h = String(d.getHours()).padStart(2, '0');
|
|
49
|
-
const m = String(d.getMinutes()).padStart(2, '0');
|
|
50
|
-
const s = String(d.getSeconds()).padStart(2, '0');
|
|
51
|
-
return `${h}:${m}:${s}`;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
type FlatRow = { kind: 'header'; blockIndex: number; text: string } | { kind: 'content'; text: string };
|
|
55
|
-
|
|
56
|
-
export class ThinkingPanel extends BasePanel {
|
|
57
|
-
private blocks: ReasoningBlock[] = [];
|
|
58
|
-
private unsubs: Array<() => void> = [];
|
|
59
|
-
private cursorIndex = 0;
|
|
60
|
-
private scrollOffset = 0;
|
|
61
|
-
private autoScroll = true;
|
|
62
|
-
private lastWidth = 80;
|
|
63
|
-
private static readonly MAX_BLOCKS = 100;
|
|
64
|
-
|
|
65
|
-
constructor(private readonly turnEvents: UiEventFeed<TurnEvent>) {
|
|
66
|
-
super('thinking', 'Thinking', 'T', 'ai');
|
|
67
|
-
this._attachBus();
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
override onActivate(): void {
|
|
71
|
-
this.needsRender = true;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
override onDeactivate(): void {
|
|
75
|
-
super.onDeactivate();
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
override onDestroy(): void {
|
|
79
|
-
this._detachBus();
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
handleInput(key: string): boolean {
|
|
83
|
-
switch (key) {
|
|
84
|
-
case 'up': this._move(-1); this.autoScroll = false; return true;
|
|
85
|
-
case 'down': this._move(1); return true;
|
|
86
|
-
case 'pageup': this._move(-10); this.autoScroll = false; return true;
|
|
87
|
-
case 'pagedown': this._move(10); return true;
|
|
88
|
-
case 'return': this._toggleCollapse(); return true;
|
|
89
|
-
case 'g': this.autoScroll = true; this.markDirty(); return true;
|
|
90
|
-
default: return false;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
render(width: number, height: number): Line[] {
|
|
95
|
-
if (height <= 0 || width <= 0) return [];
|
|
96
|
-
this.lastWidth = width;
|
|
97
|
-
|
|
98
|
-
const hasActive = this.blocks.some(b => b.active);
|
|
99
|
-
const flat = this._buildFlat(width);
|
|
100
|
-
const title = hasActive ? ' Thinking [streaming]' : ` Thinking [${this.blocks.length} blocks]`;
|
|
101
|
-
const footerLines = [
|
|
102
|
-
buildPanelLine(width, [
|
|
103
|
-
[' Up/Down', DEFAULT_PANEL_PALETTE.info],
|
|
104
|
-
[' scroll', DEFAULT_PANEL_PALETTE.dim],
|
|
105
|
-
[' Enter', DEFAULT_PANEL_PALETTE.info],
|
|
106
|
-
[' collapse', DEFAULT_PANEL_PALETTE.dim],
|
|
107
|
-
[' g', DEFAULT_PANEL_PALETTE.info],
|
|
108
|
-
[' jump to end', DEFAULT_PANEL_PALETTE.dim],
|
|
109
|
-
[this.autoScroll ? ' auto-scroll ON' : ' manual', this.autoScroll ? DEFAULT_PANEL_PALETTE.good : DEFAULT_PANEL_PALETTE.warn],
|
|
110
|
-
]),
|
|
111
|
-
];
|
|
112
|
-
|
|
113
|
-
if (flat.length === 0) {
|
|
114
|
-
return buildPanelWorkspace(width, height, {
|
|
115
|
-
title,
|
|
116
|
-
intro: 'Live reasoning blocks stream here while the model is actively thinking.',
|
|
117
|
-
sections: [
|
|
118
|
-
{
|
|
119
|
-
title: 'Reasoning',
|
|
120
|
-
lines: buildEmptyState(
|
|
121
|
-
width,
|
|
122
|
-
' No reasoning content yet',
|
|
123
|
-
'When the model emits thinking or reasoning deltas during a turn, they accumulate here in expandable per-turn blocks.',
|
|
124
|
-
[{ command: '/chat <prompt>', summary: 'start a turn with a reasoning-capable model to stream thinking here' }],
|
|
125
|
-
DEFAULT_PANEL_PALETTE,
|
|
126
|
-
),
|
|
127
|
-
},
|
|
128
|
-
],
|
|
129
|
-
footerLines,
|
|
130
|
-
palette: DEFAULT_PANEL_PALETTE,
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
if (this.autoScroll) {
|
|
135
|
-
this.cursorIndex = Math.max(0, flat.length - 1);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
this.cursorIndex = Math.max(0, Math.min(this.cursorIndex, Math.max(0, flat.length - 1)));
|
|
139
|
-
|
|
140
|
-
const summary: PanelWorkspaceSection = {
|
|
141
|
-
title: 'Summary',
|
|
142
|
-
lines: [
|
|
143
|
-
buildPanelLine(width, [
|
|
144
|
-
[' Blocks ', DEFAULT_PANEL_PALETTE.label],
|
|
145
|
-
[String(this.blocks.length), DEFAULT_PANEL_PALETTE.value],
|
|
146
|
-
[' Active ', DEFAULT_PANEL_PALETTE.label],
|
|
147
|
-
[String(this.blocks.filter((block) => block.active).length), hasActive ? DEFAULT_PANEL_PALETTE.warn : DEFAULT_PANEL_PALETTE.dim],
|
|
148
|
-
[' Mode ', DEFAULT_PANEL_PALETTE.label],
|
|
149
|
-
[this.autoScroll ? 'auto-scroll' : 'manual', this.autoScroll ? DEFAULT_PANEL_PALETTE.good : DEFAULT_PANEL_PALETTE.info],
|
|
150
|
-
]),
|
|
151
|
-
],
|
|
152
|
-
};
|
|
153
|
-
|
|
154
|
-
// Resolve the reasoning block that owns the cursor row (walk back to its
|
|
155
|
-
// header) so the detail block describes the actual turn, not just row kind.
|
|
156
|
-
let selectedBlockIndex = -1;
|
|
157
|
-
for (let i = Math.min(this.cursorIndex, flat.length - 1); i >= 0; i--) {
|
|
158
|
-
const row = flat[i];
|
|
159
|
-
if (row?.kind === 'header') { selectedBlockIndex = row.blockIndex; break; }
|
|
160
|
-
}
|
|
161
|
-
const selectedBlock = selectedBlockIndex >= 0 ? this.blocks[selectedBlockIndex] : undefined;
|
|
162
|
-
const selectedSection: PanelWorkspaceSection = {
|
|
163
|
-
title: 'Selected',
|
|
164
|
-
lines: selectedBlock
|
|
165
|
-
? [
|
|
166
|
-
buildPanelLine(width, [
|
|
167
|
-
[' Turn ', DEFAULT_PANEL_PALETTE.label],
|
|
168
|
-
[selectedBlock.turnId, DEFAULT_PANEL_PALETTE.value],
|
|
169
|
-
[' State ', DEFAULT_PANEL_PALETTE.label],
|
|
170
|
-
[selectedBlock.active ? 'streaming' : 'complete', selectedBlock.active ? DEFAULT_PANEL_PALETTE.warn : DEFAULT_PANEL_PALETTE.good],
|
|
171
|
-
[' View ', DEFAULT_PANEL_PALETTE.label],
|
|
172
|
-
[selectedBlock.collapsed ? 'collapsed' : 'expanded', DEFAULT_PANEL_PALETTE.info],
|
|
173
|
-
[' Chars ', DEFAULT_PANEL_PALETTE.label],
|
|
174
|
-
[String(selectedBlock.content.length), DEFAULT_PANEL_PALETTE.dim],
|
|
175
|
-
]),
|
|
176
|
-
buildPanelLine(width, [
|
|
177
|
-
[' Correlation ', DEFAULT_PANEL_PALETTE.label],
|
|
178
|
-
[selectedBlock.correlationId ?? 'n/a', DEFAULT_PANEL_PALETTE.dim],
|
|
179
|
-
[' Started ', DEFAULT_PANEL_PALETTE.label],
|
|
180
|
-
[fmtClock(selectedBlock.startedAt), DEFAULT_PANEL_PALETTE.dim],
|
|
181
|
-
[' Updated ', DEFAULT_PANEL_PALETTE.label],
|
|
182
|
-
[fmtClock(selectedBlock.updatedAt), DEFAULT_PANEL_PALETTE.dim],
|
|
183
|
-
]),
|
|
184
|
-
]
|
|
185
|
-
: [buildPanelLine(width, [[' No block selected', DEFAULT_PANEL_PALETTE.dim]])],
|
|
186
|
-
};
|
|
187
|
-
|
|
188
|
-
const reasoningSection = resolveScrollablePanelSection(width, height, {
|
|
189
|
-
intro: 'Live reasoning blocks stream here while the model is actively thinking.',
|
|
190
|
-
footerLines,
|
|
191
|
-
palette: DEFAULT_PANEL_PALETTE,
|
|
192
|
-
beforeSections: [summary],
|
|
193
|
-
section: {
|
|
194
|
-
title: 'Reasoning',
|
|
195
|
-
scrollableLines: flat.map((row, index) => this._renderRow(width, row, index === this.cursorIndex)),
|
|
196
|
-
selectedIndex: this.cursorIndex,
|
|
197
|
-
scrollOffset: this.scrollOffset,
|
|
198
|
-
minRows: 8,
|
|
199
|
-
},
|
|
200
|
-
afterSections: [selectedSection],
|
|
201
|
-
});
|
|
202
|
-
this.scrollOffset = reasoningSection.scrollOffset;
|
|
203
|
-
|
|
204
|
-
return buildPanelWorkspace(width, height, {
|
|
205
|
-
title,
|
|
206
|
-
intro: 'Live reasoning blocks stream here while the model is actively thinking.',
|
|
207
|
-
sections: [
|
|
208
|
-
summary,
|
|
209
|
-
reasoningSection.section,
|
|
210
|
-
selectedSection,
|
|
211
|
-
],
|
|
212
|
-
footerLines,
|
|
213
|
-
palette: DEFAULT_PANEL_PALETTE,
|
|
214
|
-
});
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
private _renderRow(width: number, row: FlatRow, isCursor: boolean): Line {
|
|
218
|
-
const bg = isCursor ? C.selectBg : '';
|
|
219
|
-
if (row.kind === 'header') {
|
|
220
|
-
const indicator = this.blocks[row.blockIndex]?.collapsed ? '▸' : '▾';
|
|
221
|
-
const active = this.blocks[row.blockIndex]?.active;
|
|
222
|
-
const bullet = active ? '\u25cf ' : '\u25e6 ';
|
|
223
|
-
return buildStyledPanelLine(width, [
|
|
224
|
-
{ text: truncateDisplay(` ${bullet}${row.text} ${indicator}`, width), fg: active ? C.activeLabel : C.turnLabel, bg, bold: true },
|
|
225
|
-
]);
|
|
226
|
-
}
|
|
227
|
-
return buildStyledPanelLine(width, [
|
|
228
|
-
{ text: ' ', fg: C.reasoningFg, bg },
|
|
229
|
-
{ text: truncateDisplay(row.text, Math.max(0, width - 2)), fg: isCursor ? C.activeFg : C.reasoningFg, bg },
|
|
230
|
-
]);
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
private _buildFlat(width: number): FlatRow[] {
|
|
234
|
-
const rows: FlatRow[] = [];
|
|
235
|
-
for (let i = 0; i < this.blocks.length; i++) {
|
|
236
|
-
const block = this.blocks[i]!;
|
|
237
|
-
const turnLabel = `Turn ${block.turnId} ${fmtClock(block.startedAt)}${block.active ? ' (streaming)' : ''}`;
|
|
238
|
-
rows.push({ kind: 'header', blockIndex: i, text: turnLabel });
|
|
239
|
-
if (!block.collapsed) {
|
|
240
|
-
const wrapped = wrapText(block.content || '(empty)', Math.max(1, width - 2));
|
|
241
|
-
for (const line of wrapped) {
|
|
242
|
-
rows.push({ kind: 'content', text: line });
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
return rows;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
private _toggleCollapse(): void {
|
|
250
|
-
// Find the header row at or before cursorIndex
|
|
251
|
-
const flat = this._buildFlat(this.lastWidth);
|
|
252
|
-
for (let i = this.cursorIndex; i >= 0; i--) {
|
|
253
|
-
const row = flat[i];
|
|
254
|
-
if (row?.kind === 'header') {
|
|
255
|
-
const block = this.blocks[row.blockIndex];
|
|
256
|
-
if (block) {
|
|
257
|
-
block.collapsed = !block.collapsed;
|
|
258
|
-
this.markDirty();
|
|
259
|
-
return;
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
private _move(delta: number): void {
|
|
266
|
-
const flat = this._buildFlat(this.lastWidth);
|
|
267
|
-
if (flat.length === 0) return;
|
|
268
|
-
this.cursorIndex = Math.max(0, Math.min(flat.length - 1, this.cursorIndex + delta));
|
|
269
|
-
this.markDirty();
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
private _attachBus(): void {
|
|
273
|
-
if (this.unsubs.length > 0) return;
|
|
274
|
-
|
|
275
|
-
let currentBlock: ReasoningBlock | null = null;
|
|
276
|
-
|
|
277
|
-
// onEnvelope (not on) so real turnId/traceId/timestamp — the same fields
|
|
278
|
-
// TasksPanel and the tool inspector join their own rows on — stamp each
|
|
279
|
-
// block instead of a locally-incrementing counter.
|
|
280
|
-
this.unsubs.push(this.turnEvents.onEnvelope('STREAM_START', (envelope) => {
|
|
281
|
-
const block: ReasoningBlock = {
|
|
282
|
-
turnId: envelope.turnId ?? envelope.payload.turnId,
|
|
283
|
-
correlationId: envelope.traceId,
|
|
284
|
-
content: '',
|
|
285
|
-
active: true,
|
|
286
|
-
collapsed: false,
|
|
287
|
-
startedAt: envelope.ts,
|
|
288
|
-
updatedAt: envelope.ts,
|
|
289
|
-
};
|
|
290
|
-
// Cap block count to prevent unbounded growth
|
|
291
|
-
if (this.blocks.length >= ThinkingPanel.MAX_BLOCKS) {
|
|
292
|
-
this.blocks.shift();
|
|
293
|
-
}
|
|
294
|
-
this.blocks.push(block);
|
|
295
|
-
currentBlock = block;
|
|
296
|
-
this.autoScroll = true;
|
|
297
|
-
this.markDirty();
|
|
298
|
-
}));
|
|
299
|
-
|
|
300
|
-
this.unsubs.push(this.turnEvents.onEnvelope('STREAM_DELTA', (envelope) => {
|
|
301
|
-
const reasoning = envelope.payload.reasoning;
|
|
302
|
-
if (reasoning && currentBlock) {
|
|
303
|
-
currentBlock.content += reasoning;
|
|
304
|
-
currentBlock.updatedAt = envelope.ts;
|
|
305
|
-
this.autoScroll = true;
|
|
306
|
-
this.markDirty();
|
|
307
|
-
}
|
|
308
|
-
}));
|
|
309
|
-
|
|
310
|
-
this.unsubs.push(this.turnEvents.onEnvelope('STREAM_END', (envelope) => {
|
|
311
|
-
if (currentBlock) {
|
|
312
|
-
currentBlock.active = false;
|
|
313
|
-
currentBlock.updatedAt = envelope.ts;
|
|
314
|
-
currentBlock = null;
|
|
315
|
-
this.markDirty();
|
|
316
|
-
}
|
|
317
|
-
}));
|
|
318
|
-
|
|
319
|
-
this.unsubs.push(this.turnEvents.onEnvelope('TURN_COMPLETED', (envelope) => {
|
|
320
|
-
if (currentBlock) {
|
|
321
|
-
currentBlock.active = false;
|
|
322
|
-
currentBlock.updatedAt = envelope.ts;
|
|
323
|
-
currentBlock = null;
|
|
324
|
-
this.markDirty();
|
|
325
|
-
}
|
|
326
|
-
}));
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
private _detachBus(): void {
|
|
330
|
-
for (const unsub of this.unsubs) unsub();
|
|
331
|
-
this.unsubs = [];
|
|
332
|
-
}
|
|
333
|
-
}
|