@pellux/goodvibes-agent 1.4.4 → 1.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -0
- package/README.md +52 -7
- package/dist/package/main.js +7491 -12253
- package/docs/README.md +2 -2
- package/docs/channels-remote-and-api.md +1 -1
- package/docs/getting-started.md +2 -2
- package/docs/release-and-publishing.md +1 -1
- package/docs/tools-and-commands.md +5 -5
- package/package.json +4 -2
- package/release/live-verification/live-verification.json +13 -13
- package/release/live-verification/live-verification.md +15 -15
- package/release/performance-snapshot.json +2 -2
- package/release/release-notes.md +5 -7
- package/release/release-readiness.json +6 -6
- package/src/agent/behavior-discovery-summary.ts +4 -18
- package/src/agent/calendar-registry.ts +322 -0
- package/src/agent/competitive-feature-inventory.ts +268 -130
- package/src/agent/document-registry.ts +5 -1
- package/src/agent/email/email-service.ts +350 -0
- package/src/agent/email/imap-client.ts +596 -0
- package/src/agent/email/smtp-client.ts +453 -0
- package/src/agent/ics-calendar.ts +662 -0
- package/src/agent/ics-timezone.ts +172 -0
- package/src/agent/markdown-frontmatter.ts +31 -0
- package/src/agent/memory-safety.ts +1 -1
- package/src/agent/note-registry.ts +3 -9
- package/src/agent/persona-discovery.ts +3 -15
- package/src/agent/persona-registry.ts +1 -10
- package/src/agent/research-source-registry.ts +5 -1
- package/src/agent/routine-discovery.ts +3 -15
- package/src/agent/runtime-profile.ts +3 -0
- package/src/agent/skill-discovery.ts +3 -15
- package/src/agent/skill-draft-proposer.ts +203 -0
- package/src/agent/skill-draft-runner.ts +201 -0
- package/src/agent/skill-registry.ts +36 -1
- package/src/agent/skill-standard.ts +99 -0
- package/src/agent/vibe-file.ts +7 -22
- package/src/cli/completion.ts +1 -1
- package/src/cli/config-overrides.ts +10 -1
- package/src/cli/redaction.ts +17 -5
- package/src/config/provider-model.ts +2 -1
- package/src/config/secret-config.ts +13 -6
- package/src/core/activity-feed.ts +97 -0
- package/src/core/away-digest.ts +161 -0
- package/src/core/conversation-rendering.ts +7 -3
- package/src/core/conversation.ts +22 -15
- package/src/core/hardware-profile.ts +362 -0
- package/src/core/last-seen-store.ts +78 -0
- package/src/core/plain-language.ts +52 -0
- package/src/core/setup-incomplete-hint.ts +90 -0
- package/src/core/system-message-router.ts +38 -87
- package/src/input/agent-workspace-basic-command-editor-submission.ts +60 -220
- package/src/input/agent-workspace-basic-command-editors.ts +39 -141
- package/src/input/agent-workspace-categories.ts +40 -125
- package/src/input/agent-workspace-command-editor.ts +4 -0
- package/src/input/agent-workspace-host-category.ts +0 -5
- package/src/input/agent-workspace-local-editor-submission.ts +3 -1
- package/src/input/agent-workspace-navigation.ts +10 -3
- package/src/input/agent-workspace-onboarding-actions.ts +132 -0
- package/src/input/agent-workspace-onboarding-categories.ts +29 -26
- package/src/input/agent-workspace-onboarding-finish.ts +11 -4
- package/src/input/agent-workspace-onboarding-state.ts +111 -0
- package/src/input/agent-workspace-profile-editor-submission.ts +260 -0
- package/src/input/agent-workspace-profile-editors.ts +155 -0
- package/src/input/agent-workspace-subscription-editor.ts +7 -0
- package/src/input/agent-workspace-types.ts +5 -1
- package/src/input/agent-workspace.ts +65 -39
- package/src/input/command-registry.ts +18 -5
- package/src/input/commands/agent-runtime-profile-runtime.ts +2 -1
- package/src/input/commands/agent-skills-runtime.ts +60 -3
- package/src/input/commands/calendar-runtime.ts +209 -0
- package/src/input/commands/channels-runtime.ts +1 -0
- package/src/input/commands/command-error.ts +9 -0
- package/src/input/commands/compat-runtime.ts +1 -0
- package/src/input/commands/config.ts +1 -0
- package/src/input/commands/conversation-runtime.ts +1 -1
- package/src/input/commands/delegation-runtime.ts +5 -6
- package/src/input/commands/email-runtime.ts +387 -0
- package/src/input/commands/experience-runtime.ts +17 -4
- package/src/input/commands/guidance-runtime.ts +1 -1
- package/src/input/commands/health-runtime.ts +6 -1
- package/src/input/commands/knowledge-format.ts +73 -0
- package/src/input/commands/knowledge.ts +9 -74
- package/src/input/commands/local-provider-runtime.ts +2 -1
- package/src/input/commands/local-runtime.ts +10 -4
- package/src/input/commands/mcp-runtime.ts +7 -0
- package/src/input/commands/notify-runtime.ts +17 -1
- package/src/input/commands/onboarding-runtime.ts +1 -0
- package/src/input/commands/operator-actions-runtime.ts +1 -0
- package/src/input/commands/operator-runtime.ts +3 -0
- package/src/input/commands/personas-runtime.ts +1 -0
- package/src/input/commands/platform-access-runtime.ts +40 -17
- package/src/input/commands/product-runtime.ts +6 -1
- package/src/input/commands/provider-accounts-runtime.ts +3 -2
- package/src/input/commands/qrcode-runtime.ts +1 -0
- package/src/input/commands/routines-runtime.ts +1 -0
- package/src/input/commands/runtime-services.ts +0 -13
- package/src/input/commands/security-runtime.ts +1 -0
- package/src/input/commands/session-content.ts +1 -0
- package/src/input/commands/shell-core.ts +5 -2
- package/src/input/commands/subscription-runtime.ts +33 -9
- package/src/input/commands/support-bundle-runtime.ts +8 -1
- package/src/input/commands/tasks-runtime.ts +1 -0
- package/src/input/commands/tts-runtime.ts +1 -0
- package/src/input/commands/vibe-runtime.ts +1 -0
- package/src/input/commands.ts +4 -0
- package/src/input/feed-context-factory.ts +3 -12
- package/src/input/handler-command-route.ts +7 -60
- package/src/input/handler-feed-routes.ts +0 -194
- package/src/input/handler-feed.ts +2 -54
- package/src/input/handler-interactions.ts +0 -2
- package/src/input/handler-modal-stack.ts +0 -9
- package/src/input/handler-picker-routes.ts +24 -1
- package/src/input/handler-shortcuts.ts +11 -40
- package/src/input/handler-ui-state.ts +13 -0
- package/src/input/handler.ts +8 -35
- package/src/input/keybindings.ts +40 -17
- package/src/input/model-picker-local-fit.ts +130 -0
- package/src/input/shell-passthrough.ts +58 -0
- package/src/input/submission-router.ts +0 -5
- package/src/main.ts +129 -90
- package/src/renderer/activity-sidebar.ts +186 -0
- package/src/renderer/agent-workspace-context-lines.ts +5 -48
- package/src/renderer/agent-workspace-style.ts +1 -1
- package/src/renderer/agent-workspace.ts +14 -2
- package/src/renderer/compositor.ts +37 -125
- package/src/renderer/conversation-overlays.ts +3 -3
- package/src/renderer/help-overlay.ts +7 -5
- package/src/renderer/model-workspace.ts +101 -86
- package/src/{panels → renderer}/polish.ts +3 -3
- package/src/renderer/shell-surface.ts +4 -5
- package/src/renderer/status-token.ts +31 -11
- package/src/renderer/tab-strip.ts +1 -1
- package/src/renderer/tool-call.ts +7 -8
- package/src/renderer/tool-labels.ts +92 -0
- package/src/renderer/ui-factory.ts +48 -96
- package/src/runtime/bootstrap-command-context.ts +0 -5
- package/src/runtime/bootstrap-command-parts.ts +6 -15
- package/src/runtime/bootstrap-core.ts +34 -13
- package/src/runtime/bootstrap-hook-bridge.ts +3 -1
- package/src/runtime/bootstrap-shell.ts +3 -50
- package/src/runtime/bootstrap.ts +3 -4
- package/src/runtime/context.ts +1 -1
- package/src/runtime/diagnostics/panels/index.ts +0 -1
- package/src/runtime/diagnostics/panels/policy.ts +1 -1
- package/src/runtime/execution-ledger.ts +16 -1
- package/src/runtime/index.ts +6 -1
- package/src/runtime/onboarding/index.ts +1 -0
- package/src/runtime/onboarding/onboarding-state.ts +200 -0
- package/src/{panels → runtime}/provider-account-snapshot.ts +5 -2
- package/src/runtime/services.ts +16 -4
- package/src/runtime/terminal-output-guard.ts +7 -0
- package/src/runtime/tool-permission-safety.ts +1 -1
- package/src/runtime/ui/model-picker/data-provider.ts +1 -1
- package/src/runtime/ui/model-picker/health-enrichment.ts +2 -2
- package/src/runtime/ui/model-picker/types.ts +2 -2
- package/src/runtime/ui/provider-health/data-provider.ts +3 -3
- package/src/runtime/ui/provider-health/types.ts +2 -2
- package/src/runtime/ui-services.ts +0 -2
- package/src/shell/agent-workspace-fullscreen.ts +0 -1
- package/src/shell/autonomy-surfacing.ts +272 -0
- package/src/shell/session-continuity-hints.ts +0 -6
- package/src/shell/startup-wiring.ts +221 -0
- package/src/shell/ui-openers.ts +18 -29
- package/src/tools/agent-context-policy.ts +1 -1
- package/src/tools/agent-harness-background-processes.ts +4 -4
- package/src/tools/agent-harness-browser-cockpit-route.ts +3 -3
- package/src/tools/agent-harness-command-runner.ts +6 -1
- package/src/tools/agent-harness-document-ops.ts +26 -53
- package/src/tools/agent-harness-execution-history.ts +1 -13
- package/src/tools/agent-harness-execution-posture.ts +0 -15
- package/src/tools/agent-harness-keybinding-metadata.ts +28 -29
- package/src/tools/agent-harness-local-model-cookbook.ts +24 -1
- package/src/tools/agent-harness-metadata.ts +16 -0
- package/src/tools/agent-harness-mode-catalog.ts +2 -9
- package/src/tools/agent-harness-provider-account-metadata.ts +2 -2
- package/src/tools/agent-harness-setup-posture-utils.ts +1 -1
- package/src/tools/agent-harness-tool-schema.ts +13 -14
- package/src/tools/agent-harness-tool.ts +27 -23
- package/src/tools/agent-harness-ui-surface-metadata.ts +10 -20
- package/src/tools/agent-harness-workspace-action-runner.ts +3 -4
- package/src/tools/agent-workspace-tool.ts +2 -33
- package/src/utils/terminal-width.ts +9 -3
- package/src/version.ts +1 -1
- package/src/input/agent-workspace-panel-route.ts +0 -44
- package/src/input/panel-integration-actions.ts +0 -26
- package/src/panels/approval-panel.ts +0 -149
- package/src/panels/automation-control-panel.ts +0 -212
- package/src/panels/base-panel.ts +0 -254
- package/src/panels/builtin/agent.ts +0 -58
- package/src/panels/builtin/knowledge.ts +0 -26
- package/src/panels/builtin/operations.ts +0 -121
- package/src/panels/builtin/session.ts +0 -138
- package/src/panels/builtin/shared.ts +0 -275
- package/src/panels/builtin/usage.ts +0 -21
- package/src/panels/builtin-panels.ts +0 -23
- package/src/panels/confirm-state.ts +0 -61
- package/src/panels/context-visualizer-panel.ts +0 -204
- package/src/panels/cost-tracker-panel.ts +0 -444
- package/src/panels/docs-panel.ts +0 -285
- package/src/panels/index.ts +0 -25
- package/src/panels/knowledge-panel.ts +0 -417
- package/src/panels/memory-panel.ts +0 -226
- package/src/panels/panel-list-panel.ts +0 -464
- package/src/panels/panel-manager.ts +0 -570
- package/src/panels/provider-accounts-panel.ts +0 -233
- package/src/panels/provider-health-domains.ts +0 -208
- package/src/panels/provider-health-panel.ts +0 -720
- package/src/panels/provider-health-tracker.ts +0 -115
- package/src/panels/provider-stats-panel.ts +0 -366
- package/src/panels/qr-panel.ts +0 -207
- package/src/panels/schedule-panel.ts +0 -321
- package/src/panels/scrollable-list-panel.ts +0 -491
- package/src/panels/search-focus.ts +0 -32
- package/src/panels/security-panel.ts +0 -295
- package/src/panels/session-browser-panel.ts +0 -395
- package/src/panels/session-maintenance.ts +0 -125
- package/src/panels/subscription-panel.ts +0 -263
- package/src/panels/system-messages-panel.ts +0 -230
- package/src/panels/tasks-panel.ts +0 -344
- package/src/panels/thinking-panel.ts +0 -304
- package/src/panels/token-budget-panel.ts +0 -475
- package/src/panels/tool-inspector-panel.ts +0 -436
- package/src/panels/types.ts +0 -54
- package/src/renderer/panel-composite.ts +0 -158
- package/src/renderer/panel-tab-bar.ts +0 -69
- package/src/renderer/panel-workspace-bar.ts +0 -42
- package/src/runtime/diagnostics/panels/panel-resources.ts +0 -118
- package/src/tools/agent-harness-panel-metadata.ts +0 -211
- /package/src/runtime/perf/{panel-health-monitor.ts → component-health.ts} +0 -0
|
@@ -1,436 +0,0 @@
|
|
|
1
|
-
// ---------------------------------------------------------------------------
|
|
2
|
-
// ToolInspectorPanel — chronological list of tool calls with args/results.
|
|
3
|
-
// ---------------------------------------------------------------------------
|
|
4
|
-
|
|
5
|
-
import type { Line } from '../types/grid.ts';
|
|
6
|
-
import { BasePanel } from './base-panel.ts';
|
|
7
|
-
import type { ToolEvent, 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
|
-
DEFAULT_PANEL_PALETTE,
|
|
16
|
-
type PanelWorkspaceSection,
|
|
17
|
-
} from './polish.ts';
|
|
18
|
-
import { truncateDisplay } from '../utils/terminal-width.ts';
|
|
19
|
-
|
|
20
|
-
const C = {
|
|
21
|
-
headerBg: '#1a1a2e',
|
|
22
|
-
headerFg: '#ffffff',
|
|
23
|
-
statusBar: '#222233',
|
|
24
|
-
statusFg: '#aaaaaa',
|
|
25
|
-
toolFg: '#00ccff',
|
|
26
|
-
resultFg: '#66ddff',
|
|
27
|
-
errorFg: '#ff6666',
|
|
28
|
-
durationFg: '#aaaa66',
|
|
29
|
-
argsFg: '#aaaaaa',
|
|
30
|
-
dimFg: '#555566',
|
|
31
|
-
selected: '#00ffff',
|
|
32
|
-
selectedBg: '#1a2a3a',
|
|
33
|
-
labelFg: '#8888bb',
|
|
34
|
-
filterFg: '#ffcc44',
|
|
35
|
-
} as const;
|
|
36
|
-
|
|
37
|
-
const MAX_ENTRIES = 500;
|
|
38
|
-
|
|
39
|
-
interface ToolCallRecord {
|
|
40
|
-
callId: string;
|
|
41
|
-
tool: string;
|
|
42
|
-
args: Record<string, unknown>;
|
|
43
|
-
startMs: number;
|
|
44
|
-
endMs?: number;
|
|
45
|
-
result?: unknown;
|
|
46
|
-
error?: string;
|
|
47
|
-
expanded: boolean;
|
|
48
|
-
approved?: boolean;
|
|
49
|
-
outputClass?: string;
|
|
50
|
-
policyAction?: string;
|
|
51
|
-
spillBackend?: string;
|
|
52
|
-
resultSummary?: string;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
type FilterMode = 'all' | string;
|
|
56
|
-
|
|
57
|
-
type FlatRow =
|
|
58
|
-
| { kind: 'call'; recordIndex: number; text: string }
|
|
59
|
-
| { kind: 'detail'; text: string; isError: boolean };
|
|
60
|
-
|
|
61
|
-
function shortTime(ms: number): string {
|
|
62
|
-
const d = new Date(ms);
|
|
63
|
-
return `${String(d.getHours()).padStart(2,'0')}:${String(d.getMinutes()).padStart(2,'0')}:${String(d.getSeconds()).padStart(2,'0')}`;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function formatMs(ms: number): string {
|
|
67
|
-
if (ms < 1000) return `${ms}ms`;
|
|
68
|
-
if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`;
|
|
69
|
-
return `${Math.floor(ms / 60000)}m${Math.floor((ms % 60000) / 1000)}s`;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function truncateJson(val: unknown, maxLen = 120): string {
|
|
73
|
-
const s = JSON.stringify(val) ?? 'null';
|
|
74
|
-
return s.length > maxLen ? s.slice(0, maxLen - 1) + '\u2026' : s;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function isResultSummaryRecord(value: unknown): value is {
|
|
78
|
-
readonly preview?: unknown;
|
|
79
|
-
readonly kind?: unknown;
|
|
80
|
-
readonly byteSize?: unknown;
|
|
81
|
-
} {
|
|
82
|
-
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function summarizeResult(result: unknown): string | undefined {
|
|
86
|
-
// SDK OBS-05 (0.21.31+): TOOL_SUCCEEDED/TOOL_FAILED.result is a ToolResultSummary
|
|
87
|
-
// { kind: 'text' | 'json' | 'binary' | 'error' | 'empty'; byteSize: number; preview?: string }.
|
|
88
|
-
if (!isResultSummaryRecord(result)) return undefined;
|
|
89
|
-
if (typeof result.preview === 'string' && result.preview.trim()) {
|
|
90
|
-
const compact = result.preview.replace(/\s+/g, ' ').trim();
|
|
91
|
-
return compact.length > 72 ? `${compact.slice(0, 69)}\u2026` : compact;
|
|
92
|
-
}
|
|
93
|
-
if (typeof result.kind === 'string' && typeof result.byteSize === 'number') {
|
|
94
|
-
return `${result.kind} (${result.byteSize}B)`;
|
|
95
|
-
}
|
|
96
|
-
return undefined;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
function detectOutputClass(tool: string, args: Record<string, unknown>): string {
|
|
100
|
-
const name = tool.toLowerCase();
|
|
101
|
-
if (name.includes('read') || name.includes('find') || name.includes('inspect') || name.includes('grep')) return 'read';
|
|
102
|
-
if (name.includes('write') || name.includes('edit') || name.includes('patch')) return 'write';
|
|
103
|
-
if (name.includes('exec') || name.includes('shell') || name.includes('bash') || typeof args.command === 'string') return 'execute';
|
|
104
|
-
if (name.includes('fetch') || name.includes('http') || name.includes('remote')) return 'network';
|
|
105
|
-
return 'analyze';
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
export class ToolInspectorPanel extends BasePanel {
|
|
109
|
-
private records: ToolCallRecord[] = [];
|
|
110
|
-
private filterMode: FilterMode = 'all';
|
|
111
|
-
private unsubs: Array<() => void> = [];
|
|
112
|
-
private cursorIndex = 0;
|
|
113
|
-
private scrollOffset = 0;
|
|
114
|
-
private autoScroll = true;
|
|
115
|
-
private _flatCache: FlatRow[] | null = null;
|
|
116
|
-
|
|
117
|
-
constructor(
|
|
118
|
-
private readonly toolEvents: UiEventFeed<ToolEvent>,
|
|
119
|
-
private readonly turnEvents: UiEventFeed<TurnEvent>,
|
|
120
|
-
) {
|
|
121
|
-
super('tools', 'Tools', 'X', 'ai');
|
|
122
|
-
this._attachBus();
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
override onActivate(): void {
|
|
126
|
-
this.needsRender = true;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
override onDeactivate(): void {
|
|
130
|
-
super.onDeactivate();
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
override onDestroy(): void {
|
|
134
|
-
this._detachBus();
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
override markDirty(): void {
|
|
138
|
-
this._flatCache = null;
|
|
139
|
-
super.markDirty();
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
handleInput(key: string): boolean {
|
|
143
|
-
switch (key) {
|
|
144
|
-
case 'up': this._move(-1); this.autoScroll = false; return true;
|
|
145
|
-
case 'down': this._move(1); return true;
|
|
146
|
-
case 'pageup': this._move(-10); this.autoScroll = false; return true;
|
|
147
|
-
case 'pagedown': this._move(10); return true;
|
|
148
|
-
case 'return': this._toggleExpand(); return true;
|
|
149
|
-
case 'c': this.records = []; this.markDirty(); return true;
|
|
150
|
-
case 'g': this.autoScroll = true; this.markDirty(); return true;
|
|
151
|
-
case 'f': this._cycleFilter(); return true;
|
|
152
|
-
default: return false;
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
render(width: number, height: number): Line[] {
|
|
157
|
-
if (height <= 0 || width <= 0) return [];
|
|
158
|
-
|
|
159
|
-
const running = this.records.filter(r => r.endMs === undefined).length;
|
|
160
|
-
const filterLabel = this.filterMode === 'all' ? '' : ` [${this.filterMode}]`;
|
|
161
|
-
const title = ` Tools [${this.records.length} calls${running > 0 ? `, ${running} running` : ''}]${filterLabel}`;
|
|
162
|
-
const footerLines = [
|
|
163
|
-
buildPanelLine(width, [
|
|
164
|
-
[' Up/Down', DEFAULT_PANEL_PALETTE.info],
|
|
165
|
-
[' scroll', DEFAULT_PANEL_PALETTE.dim],
|
|
166
|
-
[' Enter', DEFAULT_PANEL_PALETTE.info],
|
|
167
|
-
[' expand', DEFAULT_PANEL_PALETTE.dim],
|
|
168
|
-
[' f', DEFAULT_PANEL_PALETTE.info],
|
|
169
|
-
[' filter', DEFAULT_PANEL_PALETTE.dim],
|
|
170
|
-
[' c', DEFAULT_PANEL_PALETTE.info],
|
|
171
|
-
[' clear', DEFAULT_PANEL_PALETTE.dim],
|
|
172
|
-
[' g', DEFAULT_PANEL_PALETTE.info],
|
|
173
|
-
[' end', DEFAULT_PANEL_PALETTE.dim],
|
|
174
|
-
]),
|
|
175
|
-
];
|
|
176
|
-
|
|
177
|
-
const flat = this._getFlat();
|
|
178
|
-
|
|
179
|
-
if (this.autoScroll) {
|
|
180
|
-
this.cursorIndex = Math.max(0, flat.length - 1);
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
if (flat.length === 0) {
|
|
184
|
-
return buildPanelWorkspace(width, height, {
|
|
185
|
-
title,
|
|
186
|
-
intro: 'Inspect chronological tool activity, arguments, results, errors, and running calls.',
|
|
187
|
-
sections: [
|
|
188
|
-
{
|
|
189
|
-
title: 'Calls',
|
|
190
|
-
lines: buildEmptyState(
|
|
191
|
-
width,
|
|
192
|
-
' No tool calls yet',
|
|
193
|
-
'Tool executions appear here as the agent works. Expand a call to inspect its arguments and result payload.',
|
|
194
|
-
[],
|
|
195
|
-
DEFAULT_PANEL_PALETTE,
|
|
196
|
-
),
|
|
197
|
-
},
|
|
198
|
-
],
|
|
199
|
-
footerLines,
|
|
200
|
-
palette: DEFAULT_PANEL_PALETTE,
|
|
201
|
-
});
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
this.cursorIndex = Math.max(0, Math.min(this.cursorIndex, Math.max(0, flat.length - 1)));
|
|
205
|
-
const summary: PanelWorkspaceSection = {
|
|
206
|
-
title: 'Summary',
|
|
207
|
-
lines: [
|
|
208
|
-
buildPanelLine(width, [
|
|
209
|
-
[' Calls ', DEFAULT_PANEL_PALETTE.label],
|
|
210
|
-
[String(this.records.length), DEFAULT_PANEL_PALETTE.value],
|
|
211
|
-
[' Running ', DEFAULT_PANEL_PALETTE.label],
|
|
212
|
-
[String(running), running > 0 ? DEFAULT_PANEL_PALETTE.warn : DEFAULT_PANEL_PALETTE.dim],
|
|
213
|
-
[' Filter ', DEFAULT_PANEL_PALETTE.label],
|
|
214
|
-
[this.filterMode === 'all' ? 'all' : this.filterMode, this.filterMode === 'all' ? DEFAULT_PANEL_PALETTE.dim : DEFAULT_PANEL_PALETTE.info],
|
|
215
|
-
]),
|
|
216
|
-
],
|
|
217
|
-
};
|
|
218
|
-
|
|
219
|
-
const selected = flat[this.cursorIndex];
|
|
220
|
-
const detailLines: Line[] = [];
|
|
221
|
-
if (selected?.kind === 'call') {
|
|
222
|
-
const filtered = this.filterMode === 'all'
|
|
223
|
-
? this.records
|
|
224
|
-
: this.records.filter(r => r.tool === this.filterMode);
|
|
225
|
-
const rec = filtered[selected.recordIndex];
|
|
226
|
-
if (rec) {
|
|
227
|
-
detailLines.push(buildPanelLine(width, [[' Tool ', DEFAULT_PANEL_PALETTE.label], [rec.tool, DEFAULT_PANEL_PALETTE.info], [' Started ', DEFAULT_PANEL_PALETTE.label], [shortTime(rec.startMs), DEFAULT_PANEL_PALETTE.value]]));
|
|
228
|
-
detailLines.push(buildPanelLine(width, [[' Status ', DEFAULT_PANEL_PALETTE.label], [rec.endMs === undefined ? 'running' : rec.error ? 'error' : 'completed', rec.endMs === undefined ? DEFAULT_PANEL_PALETTE.warn : rec.error ? DEFAULT_PANEL_PALETTE.bad : DEFAULT_PANEL_PALETTE.good]]));
|
|
229
|
-
detailLines.push(buildPanelLine(width, [
|
|
230
|
-
[' Risk ', DEFAULT_PANEL_PALETTE.label],
|
|
231
|
-
[rec.outputClass ?? detectOutputClass(rec.tool, rec.args), DEFAULT_PANEL_PALETTE.warn],
|
|
232
|
-
[' Approved ', DEFAULT_PANEL_PALETTE.label],
|
|
233
|
-
[rec.approved === undefined ? 'unknown' : rec.approved ? 'yes' : 'no', rec.approved === false ? DEFAULT_PANEL_PALETTE.bad : DEFAULT_PANEL_PALETTE.value],
|
|
234
|
-
]));
|
|
235
|
-
if (rec.policyAction || rec.spillBackend) {
|
|
236
|
-
detailLines.push(buildPanelLine(width, [
|
|
237
|
-
[' Output ', DEFAULT_PANEL_PALETTE.label],
|
|
238
|
-
[rec.policyAction ?? 'none', rec.policyAction && rec.policyAction !== 'none' ? DEFAULT_PANEL_PALETTE.warn : DEFAULT_PANEL_PALETTE.value],
|
|
239
|
-
[' Spill ', DEFAULT_PANEL_PALETTE.label],
|
|
240
|
-
[rec.spillBackend ?? 'none', rec.spillBackend ? DEFAULT_PANEL_PALETTE.info : DEFAULT_PANEL_PALETTE.dim],
|
|
241
|
-
]));
|
|
242
|
-
}
|
|
243
|
-
if (rec.resultSummary) {
|
|
244
|
-
detailLines.push(buildPanelLine(width, [[' Summary ', DEFAULT_PANEL_PALETTE.label], [rec.resultSummary, DEFAULT_PANEL_PALETTE.value]]));
|
|
245
|
-
}
|
|
246
|
-
if (rec.error) detailLines.push(buildPanelLine(width, [[' Error ', DEFAULT_PANEL_PALETTE.bad], [rec.error, DEFAULT_PANEL_PALETTE.value]]));
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
const selectedSection: PanelWorkspaceSection = { title: 'Selected', lines: detailLines };
|
|
251
|
-
const callsSection = resolveScrollablePanelSection(width, height, {
|
|
252
|
-
intro: 'Inspect chronological tool activity, arguments, results, errors, and running calls.',
|
|
253
|
-
footerLines,
|
|
254
|
-
palette: DEFAULT_PANEL_PALETTE,
|
|
255
|
-
beforeSections: [summary],
|
|
256
|
-
section: {
|
|
257
|
-
title: 'Calls',
|
|
258
|
-
scrollableLines: flat.map((row, index) => this._renderRow(width, row, index === this.cursorIndex)),
|
|
259
|
-
selectedIndex: this.cursorIndex,
|
|
260
|
-
scrollOffset: this.scrollOffset,
|
|
261
|
-
minRows: 8,
|
|
262
|
-
},
|
|
263
|
-
afterSections: [selectedSection],
|
|
264
|
-
});
|
|
265
|
-
this.scrollOffset = callsSection.scrollOffset;
|
|
266
|
-
|
|
267
|
-
return buildPanelWorkspace(width, height, {
|
|
268
|
-
title,
|
|
269
|
-
intro: 'Inspect chronological tool activity, arguments, results, errors, and running calls.',
|
|
270
|
-
sections: [
|
|
271
|
-
summary,
|
|
272
|
-
callsSection.section,
|
|
273
|
-
selectedSection,
|
|
274
|
-
],
|
|
275
|
-
footerLines,
|
|
276
|
-
palette: DEFAULT_PANEL_PALETTE,
|
|
277
|
-
});
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
private _renderRow(width: number, row: FlatRow, isCursor: boolean): Line {
|
|
281
|
-
const bg = isCursor ? C.selectedBg : '';
|
|
282
|
-
if (row.kind === 'call') {
|
|
283
|
-
return buildStyledPanelLine(width, [
|
|
284
|
-
{ text: isCursor ? '▸' : ' ', fg: C.selected, bg, bold: isCursor },
|
|
285
|
-
{ text: truncateDisplay(row.text, Math.max(0, width - 1)), fg: isCursor ? C.selected : C.toolFg, bg, bold: isCursor },
|
|
286
|
-
]);
|
|
287
|
-
}
|
|
288
|
-
return buildStyledPanelLine(width, [
|
|
289
|
-
{ text: ' ', fg: C.argsFg, bg },
|
|
290
|
-
{ text: truncateDisplay(row.text, Math.max(0, width - 2)), fg: row.isError ? C.errorFg : C.argsFg, bg },
|
|
291
|
-
]);
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
private _getFlat(): FlatRow[] {
|
|
295
|
-
if (this._flatCache) return this._flatCache;
|
|
296
|
-
const flat: FlatRow[] = [];
|
|
297
|
-
const filtered = this.filterMode === 'all'
|
|
298
|
-
? this.records
|
|
299
|
-
: this.records.filter(r => r.tool === this.filterMode);
|
|
300
|
-
|
|
301
|
-
for (let i = 0; i < filtered.length; i++) {
|
|
302
|
-
const rec = filtered[i]!;
|
|
303
|
-
const dur = rec.endMs !== undefined ? ` (${formatMs(rec.endMs - rec.startMs)})` : ' (running)';
|
|
304
|
-
const expand = rec.expanded ? ' ▾' : ' ▸';
|
|
305
|
-
const ts = shortTime(rec.startMs);
|
|
306
|
-
const risk = rec.outputClass ?? detectOutputClass(rec.tool, rec.args);
|
|
307
|
-
const action = rec.policyAction && rec.policyAction !== 'none' ? ` ${rec.policyAction}` : '';
|
|
308
|
-
const callText = `${ts} ${rec.tool} [${risk}]${action}${dur}${expand}`;
|
|
309
|
-
flat.push({ kind: 'call', recordIndex: i, text: callText });
|
|
310
|
-
|
|
311
|
-
if (rec.expanded) {
|
|
312
|
-
const argsStr = truncateJson(rec.args, 200);
|
|
313
|
-
flat.push({ kind: 'detail', text: `Args ${argsStr}`, isError: false });
|
|
314
|
-
flat.push({ kind: 'detail', text: `Risk ${risk}${rec.approved === undefined ? '' : ` Approved ${rec.approved ? 'yes' : 'no'}`}`, isError: false });
|
|
315
|
-
if (rec.policyAction || rec.spillBackend) {
|
|
316
|
-
flat.push({
|
|
317
|
-
kind: 'detail',
|
|
318
|
-
text: `Output policy ${rec.policyAction ?? 'none'}${rec.spillBackend ? ` Spill ${rec.spillBackend}` : ''}`,
|
|
319
|
-
isError: false,
|
|
320
|
-
});
|
|
321
|
-
}
|
|
322
|
-
if (rec.resultSummary) {
|
|
323
|
-
flat.push({ kind: 'detail', text: `Summary ${rec.resultSummary}`, isError: false });
|
|
324
|
-
}
|
|
325
|
-
if (rec.result !== undefined) {
|
|
326
|
-
flat.push({ kind: 'detail', text: `Result ${truncateJson(rec.result, 200)}`, isError: false });
|
|
327
|
-
}
|
|
328
|
-
if (rec.error) {
|
|
329
|
-
flat.push({ kind: 'detail', text: `Error ${rec.error}`, isError: true });
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
this._flatCache = flat;
|
|
334
|
-
return flat;
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
private _toggleExpand(): void {
|
|
338
|
-
const flat = this._getFlat();
|
|
339
|
-
const row = flat[this.cursorIndex];
|
|
340
|
-
if (!row || row.kind !== 'call') return;
|
|
341
|
-
const rec = this.records.filter(r =>
|
|
342
|
-
this.filterMode === 'all' ? true : r.tool === this.filterMode
|
|
343
|
-
)[row.recordIndex];
|
|
344
|
-
if (rec) {
|
|
345
|
-
rec.expanded = !rec.expanded;
|
|
346
|
-
this.markDirty();
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
private _cycleFilter(): void {
|
|
351
|
-
const tools = [...new Set(this.records.map(r => r.tool))];
|
|
352
|
-
if (tools.length === 0) return;
|
|
353
|
-
if (this.filterMode === 'all') {
|
|
354
|
-
this.filterMode = tools[0]!;
|
|
355
|
-
} else {
|
|
356
|
-
const idx = tools.indexOf(this.filterMode);
|
|
357
|
-
this.filterMode = idx >= tools.length - 1 ? 'all' : tools[idx + 1]!;
|
|
358
|
-
}
|
|
359
|
-
this.markDirty();
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
private _move(delta: number): void {
|
|
363
|
-
const flat = this._getFlat();
|
|
364
|
-
if (flat.length === 0) return;
|
|
365
|
-
this.cursorIndex = Math.max(0, Math.min(flat.length - 1, this.cursorIndex + delta));
|
|
366
|
-
this.markDirty();
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
private _attachBus(): void {
|
|
370
|
-
if (this.unsubs.length > 0) return;
|
|
371
|
-
|
|
372
|
-
this.unsubs.push(this.toolEvents.on('TOOL_RECEIVED', (data) => {
|
|
373
|
-
if (this.records.length >= MAX_ENTRIES) {
|
|
374
|
-
this.records.shift();
|
|
375
|
-
}
|
|
376
|
-
this.records.push({
|
|
377
|
-
callId: data.callId,
|
|
378
|
-
tool: data.tool,
|
|
379
|
-
args: data.args,
|
|
380
|
-
startMs: Date.now(),
|
|
381
|
-
expanded: false,
|
|
382
|
-
outputClass: detectOutputClass(data.tool, data.args),
|
|
383
|
-
});
|
|
384
|
-
this.autoScroll = true;
|
|
385
|
-
this.markDirty();
|
|
386
|
-
}));
|
|
387
|
-
|
|
388
|
-
this.unsubs.push(this.toolEvents.on('TOOL_PERMISSIONED', (data) => {
|
|
389
|
-
const rec = this.records.findLast(r => r.callId === data.callId);
|
|
390
|
-
if (rec) rec.approved = data.approved;
|
|
391
|
-
this.markDirty();
|
|
392
|
-
}));
|
|
393
|
-
|
|
394
|
-
// NOTE: After SDK OBS-05 (0.21.31), TOOL_SUCCEEDED/TOOL_FAILED.result is a ToolResultSummary
|
|
395
|
-
// ({ kind, byteSize, preview? }) rather than the raw ToolResult object. The previous
|
|
396
|
-
// `_policyAudit` extraction is no longer reachable via this event — policy audit metadata
|
|
397
|
-
// must be sourced from a different channel (approval broker / tool result store) if the
|
|
398
|
-
// Tool Inspector is to display it in future.
|
|
399
|
-
this.unsubs.push(this.toolEvents.on('TOOL_SUCCEEDED', (data) => {
|
|
400
|
-
const rec = this.records.findLast(r => r.callId === data.callId);
|
|
401
|
-
if (rec) {
|
|
402
|
-
rec.endMs = Date.now();
|
|
403
|
-
rec.result = data.result;
|
|
404
|
-
rec.resultSummary = summarizeResult(data.result);
|
|
405
|
-
}
|
|
406
|
-
this.markDirty();
|
|
407
|
-
}));
|
|
408
|
-
|
|
409
|
-
this.unsubs.push(this.toolEvents.on('TOOL_FAILED', (data) => {
|
|
410
|
-
const rec = this.records.findLast(r => r.callId === data.callId);
|
|
411
|
-
if (rec) {
|
|
412
|
-
rec.endMs = Date.now();
|
|
413
|
-
rec.result = data.result;
|
|
414
|
-
rec.error = data.error;
|
|
415
|
-
rec.resultSummary = summarizeResult(data.result) ?? data.error;
|
|
416
|
-
}
|
|
417
|
-
this.markDirty();
|
|
418
|
-
}));
|
|
419
|
-
|
|
420
|
-
this.unsubs.push(this.turnEvents.on('TURN_ERROR', (data) => {
|
|
421
|
-
// Mark any running calls as errored
|
|
422
|
-
for (const rec of this.records) {
|
|
423
|
-
if (rec.endMs === undefined) {
|
|
424
|
-
rec.endMs = Date.now();
|
|
425
|
-
rec.error = data.error ?? 'unknown error';
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
this.markDirty();
|
|
429
|
-
}));
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
private _detachBus(): void {
|
|
433
|
-
for (const unsub of this.unsubs) unsub();
|
|
434
|
-
this.unsubs = [];
|
|
435
|
-
}
|
|
436
|
-
}
|
package/src/panels/types.ts
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import type { Line } from '../types/grid.ts';
|
|
2
|
-
import type { ComponentResourceContract, ComponentHealthState } from '../runtime/perf/panel-contracts.ts';
|
|
3
|
-
|
|
4
|
-
export type PanelCategory = 'agent' | 'monitoring' | 'session' | 'ai';
|
|
5
|
-
|
|
6
|
-
export interface Panel {
|
|
7
|
-
id: string;
|
|
8
|
-
name: string;
|
|
9
|
-
icon: string; // single char for tab bar
|
|
10
|
-
category: PanelCategory;
|
|
11
|
-
|
|
12
|
-
// Lifecycle
|
|
13
|
-
onActivate(): void;
|
|
14
|
-
onDeactivate(): void;
|
|
15
|
-
onDestroy(): void;
|
|
16
|
-
|
|
17
|
-
// Rendering
|
|
18
|
-
render(width: number, height: number): Line[];
|
|
19
|
-
|
|
20
|
-
// State
|
|
21
|
-
isTransient: boolean;
|
|
22
|
-
isPinned: boolean;
|
|
23
|
-
needsRender: boolean;
|
|
24
|
-
|
|
25
|
-
// Dirty-flag contract (R2: activated panel render skipping)
|
|
26
|
-
/** Mark this panel as needing a re-render on the next frame. */
|
|
27
|
-
invalidate(): void;
|
|
28
|
-
/** Called by the compositor after a successful render to clear the dirty flag. */
|
|
29
|
-
markRendered(): void;
|
|
30
|
-
|
|
31
|
-
// Resource contract (optional — panels may declare resource requirements)
|
|
32
|
-
resourceContract?: Readonly<ComponentResourceContract>;
|
|
33
|
-
|
|
34
|
-
// Health state (optional — set by ComponentHealthMonitor when panel is registered)
|
|
35
|
-
healthState?: Readonly<ComponentHealthState>;
|
|
36
|
-
|
|
37
|
-
// Input (optional)
|
|
38
|
-
handleInput?(key: string): boolean;
|
|
39
|
-
|
|
40
|
-
// Scroll input (optional)
|
|
41
|
-
// Positive delta scrolls down; negative delta scrolls up.
|
|
42
|
-
handleScroll?(deltaRows: number): boolean;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export interface PanelRegistration extends Pick<Panel, 'id' | 'name' | 'icon' | 'category'> {
|
|
46
|
-
factory: () => Panel;
|
|
47
|
-
description: string;
|
|
48
|
-
/**
|
|
49
|
-
* Instantiate this panel during bootstrap and retain the instance when it is
|
|
50
|
-
* closed so its background data continues to accumulate before the user
|
|
51
|
-
* actively opens the workspace.
|
|
52
|
-
*/
|
|
53
|
-
preload?: boolean;
|
|
54
|
-
}
|
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
import type { Line } from '../types/grid.ts';
|
|
2
|
-
import type { Panel } from '../panels/types.ts';
|
|
3
|
-
import type { InputHandler } from '../input/handler.ts';
|
|
4
|
-
import type { PanelManager } from '../panels/panel-manager.ts';
|
|
5
|
-
import type { PanelCompositeData } from './compositor.ts';
|
|
6
|
-
import { createSplitPaneLayout } from './layout-engine.ts';
|
|
7
|
-
import { renderPanelTabBar } from './panel-tab-bar.ts';
|
|
8
|
-
import { renderPanelWorkspaceBar } from './panel-workspace-bar.ts';
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* R2: Per-panel render cache for dirty-flag skipping.
|
|
12
|
-
*
|
|
13
|
-
* Maintainability hazard — mid-render invalidation race:
|
|
14
|
-
*
|
|
15
|
-
* If an event listener (e.g. a runtime-bus subscriber) fires DURING a panel's
|
|
16
|
-
* `render()` call and mutates state such that `needsRender = true`, the
|
|
17
|
-
* trailing `panel.markRendered()` below will clobber that invalidation back
|
|
18
|
-
* to `false`, causing the next frame to serve stale cached output until
|
|
19
|
-
* something else invalidates the panel.
|
|
20
|
-
*
|
|
21
|
-
* In practice this is rare because `render()` is synchronous and short, and
|
|
22
|
-
* runtime-bus listeners that mutate panel state typically run outside the
|
|
23
|
-
* render pass. Panels that DO expect to mutate from within their own render
|
|
24
|
-
* path (e.g. deferred lazy-load) should call `this.invalidate()` AFTER the
|
|
25
|
-
* trailing work so the next frame picks it up.
|
|
26
|
-
*
|
|
27
|
-
* The fix (now applied): snapshot `needsRender` before calling `render()` and
|
|
28
|
-
* only call `markRendered()` when no concurrent invalidation occurred during
|
|
29
|
-
* the render pass — preserving any mid-render invalidation.
|
|
30
|
-
*/
|
|
31
|
-
interface PanelRenderCache {
|
|
32
|
-
lines: Line[];
|
|
33
|
-
width: number;
|
|
34
|
-
height: number;
|
|
35
|
-
}
|
|
36
|
-
const panelRenderCache = new WeakMap<Panel, PanelRenderCache>();
|
|
37
|
-
/**
|
|
38
|
-
* Per-panel render-generation counter. Incremented whenever invalidate() fires
|
|
39
|
-
* on the panel (tracked here externally since Panel does not expose a version).
|
|
40
|
-
* We monkey-patch each panel the first time it enters renderPanel() by wrapping
|
|
41
|
-
* its invalidate() to bump the counter stored in this map.
|
|
42
|
-
*
|
|
43
|
-
* Race-guard: snapshot the generation before render(), compare after. If it
|
|
44
|
-
* changed, a mid-render invalidation occurred — leave needsRender=true.
|
|
45
|
-
*/
|
|
46
|
-
const panelRenderGen = new WeakMap<Panel, { gen: number }>();
|
|
47
|
-
|
|
48
|
-
function getRenderGenState(panel: Panel): { gen: number } {
|
|
49
|
-
let state = panelRenderGen.get(panel);
|
|
50
|
-
if (!state) {
|
|
51
|
-
state = { gen: 0 };
|
|
52
|
-
panelRenderGen.set(panel, state);
|
|
53
|
-
// Wrap invalidate() to bump generation counter.
|
|
54
|
-
const origInvalidate = panel.invalidate.bind(panel);
|
|
55
|
-
panel.invalidate = () => {
|
|
56
|
-
state!.gen++;
|
|
57
|
-
origInvalidate();
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
return state;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/** R2: Render a panel, skipping if nothing changed. Returns cached lines on a skip. */
|
|
64
|
-
export function renderPanel(panel: Panel, width: number, height: number): Line[] {
|
|
65
|
-
const cached = panelRenderCache.get(panel);
|
|
66
|
-
if (cached && !panel.needsRender && cached.width === width && cached.height === height) {
|
|
67
|
-
return cached.lines;
|
|
68
|
-
}
|
|
69
|
-
// Snapshot render-generation counter BEFORE calling render(). If an event
|
|
70
|
-
// listener fires during render() and calls panel.invalidate(), the generation
|
|
71
|
-
// counter will be bumped. We compare after render to detect mid-render races.
|
|
72
|
-
const genState = getRenderGenState(panel);
|
|
73
|
-
const genBefore = genState.gen;
|
|
74
|
-
const lines = panel.render(width, height);
|
|
75
|
-
// Only call markRendered() when no mid-render invalidation occurred.
|
|
76
|
-
// If the generation changed during render(), a concurrent invalidate() fired —
|
|
77
|
-
// leave needsRender=true so the next frame re-renders with the new state.
|
|
78
|
-
if (genState.gen === genBefore) {
|
|
79
|
-
panel.markRendered();
|
|
80
|
-
}
|
|
81
|
-
// If gen changed, needsRender is already true (invalidate() set it); do not
|
|
82
|
-
// call markRendered() — the next frame will pick it up.
|
|
83
|
-
panelRenderCache.set(panel, { lines, width, height });
|
|
84
|
-
return lines;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
export interface PanelCompositeBuildResult {
|
|
88
|
-
readonly panelData?: PanelCompositeData;
|
|
89
|
-
readonly panelWidth?: number;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
export function buildPanelCompositeData(
|
|
93
|
-
panelManager: PanelManager,
|
|
94
|
-
input: InputHandler,
|
|
95
|
-
panelWidth: number,
|
|
96
|
-
panelHeight: number,
|
|
97
|
-
): PanelCompositeBuildResult {
|
|
98
|
-
if (!panelManager.isVisible() || panelManager.getAllOpen().length === 0 || panelWidth <= 0) {
|
|
99
|
-
return {};
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
const topPane = panelManager.getTopPane();
|
|
103
|
-
const bottomPane = panelManager.getBottomPane();
|
|
104
|
-
const focusedPane = panelManager.getFocusedPane();
|
|
105
|
-
const workspaceTabs = panelManager.getWorkspaceTabs();
|
|
106
|
-
const verticalSplitRatio = panelManager.getVerticalSplitRatio();
|
|
107
|
-
const hasBottom = panelManager.isBottomPaneVisible() && bottomPane.panels.length > 0;
|
|
108
|
-
const workspaceBar = renderPanelWorkspaceBar(workspaceTabs, panelWidth, input.panelFocused);
|
|
109
|
-
|
|
110
|
-
let topContent: Line[];
|
|
111
|
-
let topTabBar: Line | undefined;
|
|
112
|
-
let bottomTabBar: Line | undefined;
|
|
113
|
-
let bottomContent: Line[] | undefined;
|
|
114
|
-
|
|
115
|
-
const topActivePanel = topPane.panels[topPane.activeIndex] ?? null;
|
|
116
|
-
|
|
117
|
-
if (hasBottom) {
|
|
118
|
-
topTabBar = renderPanelTabBar(
|
|
119
|
-
topPane.panels,
|
|
120
|
-
topPane.activeIndex,
|
|
121
|
-
panelWidth,
|
|
122
|
-
input.panelFocused && focusedPane === 'top',
|
|
123
|
-
'top',
|
|
124
|
-
);
|
|
125
|
-
const paneLayout = createSplitPaneLayout(Math.max(0, panelHeight - 1), verticalSplitRatio);
|
|
126
|
-
const topH = paneLayout.topContentRows;
|
|
127
|
-
const bottomH = paneLayout.bottomContentRows;
|
|
128
|
-
topContent = topActivePanel ? renderPanel(topActivePanel, panelWidth, topH) : [];
|
|
129
|
-
|
|
130
|
-
const bottomActivePanel = bottomPane.panels[bottomPane.activeIndex] ?? null;
|
|
131
|
-
bottomTabBar = renderPanelTabBar(
|
|
132
|
-
bottomPane.panels,
|
|
133
|
-
bottomPane.activeIndex,
|
|
134
|
-
panelWidth,
|
|
135
|
-
input.panelFocused && focusedPane === 'bottom',
|
|
136
|
-
'bottom',
|
|
137
|
-
);
|
|
138
|
-
bottomContent = bottomActivePanel ? renderPanel(bottomActivePanel, panelWidth, bottomH) : [];
|
|
139
|
-
} else {
|
|
140
|
-
const topH = Math.max(0, panelHeight - 1);
|
|
141
|
-
topContent = topActivePanel ? renderPanel(topActivePanel, panelWidth, topH) : [];
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
return {
|
|
145
|
-
panelData: {
|
|
146
|
-
workspaceBar,
|
|
147
|
-
topTabBar,
|
|
148
|
-
topContent,
|
|
149
|
-
topFocused: input.panelFocused && focusedPane === 'top',
|
|
150
|
-
bottomTabBar,
|
|
151
|
-
bottomContent,
|
|
152
|
-
bottomFocused: input.panelFocused && focusedPane === 'bottom',
|
|
153
|
-
separator: true,
|
|
154
|
-
verticalSplitRatio,
|
|
155
|
-
},
|
|
156
|
-
panelWidth,
|
|
157
|
-
};
|
|
158
|
-
}
|