@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,464 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* PanelListPanel — Shows registered panel routes grouped by category.
|
|
3
|
-
*
|
|
4
|
-
* Features:
|
|
5
|
-
* - Arrow keys to navigate, Enter to show the matching Agent workspace route
|
|
6
|
-
* - Open/closed indicator (● open, ○ closed)
|
|
7
|
-
* - Search/filter by typing
|
|
8
|
-
* - Grouped by category
|
|
9
|
-
*
|
|
10
|
-
* Open the current operator workspace via /agent.
|
|
11
|
-
*/
|
|
12
|
-
import type { Line } from '../types/grid.ts';
|
|
13
|
-
import type { PanelCategory, PanelRegistration } from './types.ts';
|
|
14
|
-
import { BasePanel } from './base-panel.ts';
|
|
15
|
-
import type { PanelManager } from './panel-manager.ts';
|
|
16
|
-
import type { ComponentHealthMonitor } from '../runtime/perf/panel-health-monitor.ts';
|
|
17
|
-
import { createEmptyLine } from '../types/grid.ts';
|
|
18
|
-
import {
|
|
19
|
-
buildEmptyState,
|
|
20
|
-
buildKeyValueLine,
|
|
21
|
-
buildPanelListRow,
|
|
22
|
-
buildPanelLine,
|
|
23
|
-
buildSearchInputLine,
|
|
24
|
-
buildSummaryBlock,
|
|
25
|
-
buildPanelWorkspace,
|
|
26
|
-
DEFAULT_PANEL_PALETTE,
|
|
27
|
-
resolvePrimaryScrollableSection,
|
|
28
|
-
type PanelWorkspaceSection,
|
|
29
|
-
} from './polish.ts';
|
|
30
|
-
import { truncateDisplay } from '../utils/terminal-width.ts';
|
|
31
|
-
import { wrapWithHangingIndent } from '../renderer/text-layout.ts';
|
|
32
|
-
import {
|
|
33
|
-
getPanelSearchFocusTransition,
|
|
34
|
-
isPanelSearchBackspace,
|
|
35
|
-
isPanelSearchCancel,
|
|
36
|
-
isPanelSearchCommit,
|
|
37
|
-
isPanelSearchPrintable,
|
|
38
|
-
} from './search-focus.ts';
|
|
39
|
-
import { agentWorkspaceCategoryForPanel, agentWorkspaceCommandForPanel } from '../input/agent-workspace-panel-route.ts';
|
|
40
|
-
|
|
41
|
-
// ── Colour palette ────────────────────────────────────────────────────────────
|
|
42
|
-
const C = {
|
|
43
|
-
...DEFAULT_PANEL_PALETTE,
|
|
44
|
-
header: '#94a3b8',
|
|
45
|
-
headerBg: '#1e293b',
|
|
46
|
-
category: '#64748b',
|
|
47
|
-
categoryBg: '#1e293b',
|
|
48
|
-
icon: '#38bdf8',
|
|
49
|
-
name: '#e2e8f0',
|
|
50
|
-
desc: '#64748b',
|
|
51
|
-
openDot: '#22c55e',
|
|
52
|
-
closedDot: '#475569',
|
|
53
|
-
selected: '#e2e8f0',
|
|
54
|
-
selectedBg: '#1e3a5f',
|
|
55
|
-
selIcon: '#38bdf8',
|
|
56
|
-
hint: '#475569',
|
|
57
|
-
search: '#f97316',
|
|
58
|
-
searchBg: '#1e293b',
|
|
59
|
-
dim: '#334155',
|
|
60
|
-
intro: '#94a3b8',
|
|
61
|
-
} as const;
|
|
62
|
-
|
|
63
|
-
const CATEGORY_ORDER: PanelCategory[] = ['agent', 'monitoring', 'session', 'ai'];
|
|
64
|
-
const CATEGORY_LABELS: Record<PanelCategory, string> = {
|
|
65
|
-
agent: 'Agent',
|
|
66
|
-
monitoring: 'Monitoring',
|
|
67
|
-
session: 'Session',
|
|
68
|
-
ai: 'AI',
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
// ── Layout constants ──────────────────────────────────────────────────────────
|
|
72
|
-
const NAME_COL_WIDTH = 22;
|
|
73
|
-
const PREFIX_WIDTH = 4; // arrow + dot + space + space
|
|
74
|
-
|
|
75
|
-
/** A flat entry in the navigable list — either a category header or a panel row. */
|
|
76
|
-
type ListEntry =
|
|
77
|
-
| { kind: 'header'; category: PanelCategory }
|
|
78
|
-
| { kind: 'panel'; reg: PanelRegistration };
|
|
79
|
-
|
|
80
|
-
function wrapPanelDescription(text: string, width: number, maxLines = 2): string[] {
|
|
81
|
-
if (width <= 0) return [''];
|
|
82
|
-
const lines = wrapWithHangingIndent(text, width, '', maxLines);
|
|
83
|
-
return lines.length > 0 ? lines.map((line) => truncateDisplay(line, width)) : [''];
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// ── PanelListPanel ────────────────────────────────────────────────────────────
|
|
87
|
-
|
|
88
|
-
export class PanelListPanel extends BasePanel {
|
|
89
|
-
private _selectedIndex = 0;
|
|
90
|
-
private _scrollOffset = 0;
|
|
91
|
-
private _query = '';
|
|
92
|
-
private _filterFocused = false;
|
|
93
|
-
private _routeMessage = 'This list is read-only in Agent. Press Enter for the matching Agent workspace route.';
|
|
94
|
-
private _cachedEntries: ListEntry[] | null = null;
|
|
95
|
-
private _entriesDirty = true;
|
|
96
|
-
|
|
97
|
-
public constructor(
|
|
98
|
-
private readonly panelManager: PanelManager,
|
|
99
|
-
componentHealthMonitor?: ComponentHealthMonitor,
|
|
100
|
-
) {
|
|
101
|
-
super('panel-list', 'Panel List', 'L', 'session', componentHealthMonitor);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
public override onActivate(): void {
|
|
105
|
-
super.onActivate();
|
|
106
|
-
this._selectedIndex = 0;
|
|
107
|
-
this._scrollOffset = 0;
|
|
108
|
-
this._query = '';
|
|
109
|
-
this._filterFocused = false;
|
|
110
|
-
this._routeMessage = 'This list is read-only in Agent. Press Enter for the matching Agent workspace route.';
|
|
111
|
-
this._entriesDirty = true;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
protected override markDirty(): void {
|
|
115
|
-
super.markDirty();
|
|
116
|
-
this._entriesDirty = true;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
public handleInput(key: string): boolean {
|
|
120
|
-
const entries = this._buildEntries();
|
|
121
|
-
const panelCount = entries.filter(e => e.kind === 'panel').length;
|
|
122
|
-
|
|
123
|
-
if (this._filterFocused) {
|
|
124
|
-
const transition = getPanelSearchFocusTransition(key, { selectedIndex: this._selectedIndex, itemCount: panelCount });
|
|
125
|
-
if (transition === 'focus-list') {
|
|
126
|
-
this._filterFocused = false;
|
|
127
|
-
this._selectedIndex = 0;
|
|
128
|
-
this._scrollOffset = 0;
|
|
129
|
-
this.markDirty();
|
|
130
|
-
return true;
|
|
131
|
-
}
|
|
132
|
-
if (isPanelSearchBackspace(key)) {
|
|
133
|
-
if (this._query.length === 0) return true;
|
|
134
|
-
this._query = this._query.slice(0, -1);
|
|
135
|
-
this._clampSelection(entries);
|
|
136
|
-
this.markDirty();
|
|
137
|
-
return true;
|
|
138
|
-
}
|
|
139
|
-
if (isPanelSearchCancel(key)) {
|
|
140
|
-
this._filterFocused = false;
|
|
141
|
-
this.markDirty();
|
|
142
|
-
return true;
|
|
143
|
-
}
|
|
144
|
-
if (isPanelSearchCommit(key)) {
|
|
145
|
-
this._filterFocused = false;
|
|
146
|
-
const selectedPanel = this._getSelectedPanelEntry(this._buildEntries());
|
|
147
|
-
if (selectedPanel) {
|
|
148
|
-
this._setRouteMessage(selectedPanel.reg);
|
|
149
|
-
}
|
|
150
|
-
this.markDirty();
|
|
151
|
-
return true;
|
|
152
|
-
}
|
|
153
|
-
if (isPanelSearchPrintable(key)) {
|
|
154
|
-
this._query += key;
|
|
155
|
-
this._selectedIndex = 0;
|
|
156
|
-
this._scrollOffset = 0;
|
|
157
|
-
this.markDirty();
|
|
158
|
-
return true;
|
|
159
|
-
}
|
|
160
|
-
return false;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
const transition = getPanelSearchFocusTransition(key, { selectedIndex: this._selectedIndex, itemCount: panelCount });
|
|
164
|
-
if (transition === 'focus-search') {
|
|
165
|
-
this._filterFocused = true;
|
|
166
|
-
this.markDirty();
|
|
167
|
-
return true;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// Navigation
|
|
171
|
-
if (key === 'up' || key === 'k') {
|
|
172
|
-
this._movePrevPanel();
|
|
173
|
-
this.markDirty();
|
|
174
|
-
return true;
|
|
175
|
-
}
|
|
176
|
-
if (key === 'down' || key === 'j') {
|
|
177
|
-
this._moveNextPanel(entries);
|
|
178
|
-
this.markDirty();
|
|
179
|
-
return true;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
// Route selected panel through the Agent workspace.
|
|
183
|
-
if (key === 'return' || key === 'enter') {
|
|
184
|
-
const selectedPanel = this._getSelectedPanelEntry(entries);
|
|
185
|
-
if (selectedPanel) {
|
|
186
|
-
this._setRouteMessage(selectedPanel.reg);
|
|
187
|
-
this.markDirty();
|
|
188
|
-
}
|
|
189
|
-
return true;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
if (key === 'T' || key === 'B' || key === 'M' || key === 'S' || key === 'tab') {
|
|
193
|
-
const selectedPanel = this._getSelectedPanelEntry(entries);
|
|
194
|
-
if (selectedPanel) {
|
|
195
|
-
const command = agentWorkspaceCommandForPanel(selectedPanel.reg.id);
|
|
196
|
-
this._routeMessage = `Panel placement keys are inactive in Agent. Use ${command} for ${selectedPanel.reg.name}.`;
|
|
197
|
-
} else {
|
|
198
|
-
this._routeMessage = 'Panel placement keys are inactive in Agent. Use /agent for current operator controls.';
|
|
199
|
-
}
|
|
200
|
-
this.markDirty();
|
|
201
|
-
return true;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
// Search: backspace
|
|
205
|
-
if (isPanelSearchBackspace(key)) {
|
|
206
|
-
if (this._query.length > 0) {
|
|
207
|
-
this._query = this._query.slice(0, -1);
|
|
208
|
-
this._clampSelection(entries);
|
|
209
|
-
this.markDirty();
|
|
210
|
-
return true;
|
|
211
|
-
}
|
|
212
|
-
return false;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
// Escape: clear search
|
|
216
|
-
if (isPanelSearchCancel(key)) {
|
|
217
|
-
if (this._query.length > 0) {
|
|
218
|
-
this._query = '';
|
|
219
|
-
this._clampSelection(entries);
|
|
220
|
-
this.markDirty();
|
|
221
|
-
return true;
|
|
222
|
-
}
|
|
223
|
-
return false;
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
// Printable character: append to search query
|
|
227
|
-
return false;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
public render(width: number, height: number): Line[] {
|
|
231
|
-
if (!this.canRenderNow()) {
|
|
232
|
-
return Array.from({ length: height }, () => createEmptyLine(width));
|
|
233
|
-
}
|
|
234
|
-
const start = Date.now();
|
|
235
|
-
this.needsRender = false;
|
|
236
|
-
const intro = 'Browse registered panel routes and jump to the matching Agent workspace area.';
|
|
237
|
-
const entries = this._buildEntries();
|
|
238
|
-
|
|
239
|
-
if (entries.length === 0) {
|
|
240
|
-
const lines = buildPanelWorkspace(width, height, {
|
|
241
|
-
title: 'Agent Panel Routes',
|
|
242
|
-
intro,
|
|
243
|
-
sections: [{
|
|
244
|
-
title: 'Filter',
|
|
245
|
-
lines: [buildSearchInputLine(width, 'Filter: ', `${this._query}${this._filterFocused ? '_' : ''}`, C, {
|
|
246
|
-
active: this._filterFocused,
|
|
247
|
-
bg: C.searchBg,
|
|
248
|
-
emptyLabel: this._filterFocused ? '(type to filter)' : '(/ or up at top)',
|
|
249
|
-
valueColor: C.search,
|
|
250
|
-
})],
|
|
251
|
-
}, {
|
|
252
|
-
lines: buildEmptyState(
|
|
253
|
-
width,
|
|
254
|
-
' No panels match filter.',
|
|
255
|
-
'Clear the filter or search for another panel by id, name, description, or category.',
|
|
256
|
-
[{ command: '/agent', summary: 'open the Agent workspace' }],
|
|
257
|
-
C,
|
|
258
|
-
),
|
|
259
|
-
}],
|
|
260
|
-
palette: C,
|
|
261
|
-
});
|
|
262
|
-
while (lines.length < height) lines.push(createEmptyLine(width));
|
|
263
|
-
if (lines.length > height) lines.length = height;
|
|
264
|
-
this.reportRenderDuration(Date.now() - start);
|
|
265
|
-
return lines;
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
const panelEntries = entries.filter(e => e.kind === 'panel');
|
|
269
|
-
const pm = this.panelManager;
|
|
270
|
-
const topIds = new Set(pm.getTopPane().panels.map(p => p.id));
|
|
271
|
-
const bottomIds = new Set(pm.getBottomPane().panels.map(p => p.id));
|
|
272
|
-
const selectedPanel = this._getSelectedPanelEntry(entries);
|
|
273
|
-
const selectedRoute = selectedPanel ? agentWorkspaceCommandForPanel(selectedPanel.reg.id) : '/agent';
|
|
274
|
-
const selectedArea = selectedPanel ? agentWorkspaceCategoryForPanel(selectedPanel.reg.id) : 'home';
|
|
275
|
-
const footerLines = [buildPanelLine(width, [[` [${this._selectedIndex + 1}/${panelEntries.length}] ↑/↓ nav Enter route / filter Esc clear`.slice(0, width), C.hint]])];
|
|
276
|
-
const postureLines: Line[] = [
|
|
277
|
-
buildKeyValueLine(width, [
|
|
278
|
-
{ label: 'copied open', value: String(pm.getAllOpen().length), valueColor: pm.getAllOpen().length > 0 ? C.name : C.dim },
|
|
279
|
-
{ label: 'agent route', value: selectedRoute, valueColor: C.info },
|
|
280
|
-
{ label: 'area', value: selectedArea, valueColor: C.value },
|
|
281
|
-
{ label: 'results', value: String(panelEntries.length), valueColor: C.value },
|
|
282
|
-
], C),
|
|
283
|
-
buildPanelLine(width, [[` ${truncateDisplay(this._routeMessage, Math.max(0, width - 1))}`, C.intro]]),
|
|
284
|
-
];
|
|
285
|
-
const entryLines: Line[] = [
|
|
286
|
-
buildSearchInputLine(width, 'Filter: ', `${this._query}${this._filterFocused ? '_' : ''}`, C, {
|
|
287
|
-
active: this._filterFocused,
|
|
288
|
-
bg: C.searchBg,
|
|
289
|
-
emptyLabel: this._filterFocused ? '(type to filter)' : '(/ or up at top)',
|
|
290
|
-
valueColor: C.search,
|
|
291
|
-
}),
|
|
292
|
-
];
|
|
293
|
-
const renderedBlocks: Array<{ entry: ListEntry; lines: Line[]; panelFlatIndex?: number }> = [];
|
|
294
|
-
let flatPanelIndex = 0;
|
|
295
|
-
for (const entry of entries) {
|
|
296
|
-
if (entry.kind === 'header') {
|
|
297
|
-
const label = ` ── ${CATEGORY_LABELS[entry.category]} ${'─'.repeat(Math.max(0, width - 6 - CATEGORY_LABELS[entry.category].length))}`;
|
|
298
|
-
renderedBlocks.push({
|
|
299
|
-
entry,
|
|
300
|
-
lines: [buildPanelLine(width, [[label.slice(0, width), C.category, C.categoryBg]])],
|
|
301
|
-
});
|
|
302
|
-
} else {
|
|
303
|
-
const flatIdx = flatPanelIndex++;
|
|
304
|
-
const isSelected = flatIdx === this._selectedIndex;
|
|
305
|
-
const isTopOpen = topIds.has(entry.reg.id);
|
|
306
|
-
const isBottomOpen = bottomIds.has(entry.reg.id);
|
|
307
|
-
const dot = isTopOpen || isBottomOpen ? '●' : '○';
|
|
308
|
-
const dotColor = isTopOpen || isBottomOpen ? C.openDot : C.closedDot;
|
|
309
|
-
const nameColor = isSelected ? C.selected : C.name;
|
|
310
|
-
const nameStr = entry.reg.name.padEnd(NAME_COL_WIDTH, ' ').slice(0, NAME_COL_WIDTH);
|
|
311
|
-
const descStartCol = PREFIX_WIDTH + NAME_COL_WIDTH + 1;
|
|
312
|
-
const descWidth = Math.max(1, width - descStartCol);
|
|
313
|
-
const descLines = wrapPanelDescription(entry.reg.description, descWidth, 2);
|
|
314
|
-
const routeCategory = agentWorkspaceCategoryForPanel(entry.reg.id);
|
|
315
|
-
const blockLines: Line[] = [
|
|
316
|
-
buildPanelListRow(width, [
|
|
317
|
-
{ text: dot, fg: dotColor },
|
|
318
|
-
{ text: routeCategory.charAt(0).toUpperCase(), fg: isSelected ? C.selIcon : C.icon },
|
|
319
|
-
{ text: ' ', fg: C.dim },
|
|
320
|
-
{ text: `${nameStr} `, fg: nameColor },
|
|
321
|
-
{ text: descLines[0] ?? '', fg: C.desc },
|
|
322
|
-
], C, { selected: isSelected, selectedBg: C.selectedBg, markerColor: C.selIcon }),
|
|
323
|
-
];
|
|
324
|
-
if ((descLines[1] ?? '').length > 0) {
|
|
325
|
-
blockLines.push(buildPanelLine(width, [
|
|
326
|
-
[' '.repeat(PREFIX_WIDTH), C.dim, isSelected ? C.selectedBg : C.surfaceBg],
|
|
327
|
-
[' '.repeat(NAME_COL_WIDTH), C.dim, isSelected ? C.selectedBg : C.surfaceBg],
|
|
328
|
-
[' ', C.dim, isSelected ? C.selectedBg : C.surfaceBg],
|
|
329
|
-
[descLines[1] ?? '', C.desc, isSelected ? C.selectedBg : C.surfaceBg],
|
|
330
|
-
]));
|
|
331
|
-
}
|
|
332
|
-
renderedBlocks.push({ entry, lines: blockLines, panelFlatIndex: flatIdx });
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
const blockStarts: number[] = [];
|
|
336
|
-
let totalRows = 0;
|
|
337
|
-
for (const block of renderedBlocks) {
|
|
338
|
-
blockStarts.push(totalRows);
|
|
339
|
-
totalRows += block.lines.length;
|
|
340
|
-
}
|
|
341
|
-
const selectedEntryIndex = renderedBlocks.findIndex((block) => block.panelFlatIndex === this._selectedIndex);
|
|
342
|
-
const selectedRow = selectedEntryIndex >= 0 ? blockStarts[selectedEntryIndex] ?? 0 : 0;
|
|
343
|
-
const postureSection: PanelWorkspaceSection = { lines: buildSummaryBlock(width, 'Route posture', postureLines, C) };
|
|
344
|
-
const resolvedSection = resolvePrimaryScrollableSection(width, height, {
|
|
345
|
-
intro,
|
|
346
|
-
footerLines,
|
|
347
|
-
palette: C,
|
|
348
|
-
beforeSections: [postureSection],
|
|
349
|
-
section: {
|
|
350
|
-
title: 'Panels',
|
|
351
|
-
fixedLines: entryLines,
|
|
352
|
-
scrollableLines: renderedBlocks.flatMap((block) => block.lines),
|
|
353
|
-
selectedIndex: selectedRow,
|
|
354
|
-
scrollOffset: this._scrollOffset,
|
|
355
|
-
guardRows: 1,
|
|
356
|
-
minRows: 1,
|
|
357
|
-
appendWindowSummary: {
|
|
358
|
-
dimColor: C.dim,
|
|
359
|
-
},
|
|
360
|
-
},
|
|
361
|
-
});
|
|
362
|
-
this._scrollOffset = resolvedSection.scrollOffset;
|
|
363
|
-
|
|
364
|
-
const sections: PanelWorkspaceSection[] = [
|
|
365
|
-
postureSection,
|
|
366
|
-
resolvedSection.section,
|
|
367
|
-
];
|
|
368
|
-
const lines = buildPanelWorkspace(width, height, {
|
|
369
|
-
title: 'Agent Panel Routes',
|
|
370
|
-
intro,
|
|
371
|
-
sections,
|
|
372
|
-
footerLines,
|
|
373
|
-
palette: C,
|
|
374
|
-
});
|
|
375
|
-
|
|
376
|
-
while (lines.length < height) lines.push(createEmptyLine(width));
|
|
377
|
-
if (lines.length > height) lines.length = height;
|
|
378
|
-
this.reportRenderDuration(Date.now() - start);
|
|
379
|
-
return lines;
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
// ── Private helpers ───────────────────────────────────────────────────────
|
|
383
|
-
|
|
384
|
-
/** Build flat list of entries (headers + panel rows) filtered by query. */
|
|
385
|
-
private _buildEntries(): ListEntry[] {
|
|
386
|
-
if (this._cachedEntries !== null && !this._entriesDirty) {
|
|
387
|
-
return this._cachedEntries;
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
const manager = this.panelManager;
|
|
391
|
-
const byCategory = manager.getTypesByCategory();
|
|
392
|
-
const q = this._query.toLowerCase();
|
|
393
|
-
const entries: ListEntry[] = [];
|
|
394
|
-
|
|
395
|
-
for (const cat of CATEGORY_ORDER) {
|
|
396
|
-
const regs = byCategory.get(cat) ?? [];
|
|
397
|
-
const filtered = q
|
|
398
|
-
? regs.filter(r =>
|
|
399
|
-
r.id.toLowerCase().includes(q) ||
|
|
400
|
-
r.name.toLowerCase().includes(q) ||
|
|
401
|
-
r.description.toLowerCase().includes(q) ||
|
|
402
|
-
r.category.toLowerCase().includes(q)
|
|
403
|
-
)
|
|
404
|
-
: regs;
|
|
405
|
-
|
|
406
|
-
if (filtered.length === 0) continue;
|
|
407
|
-
|
|
408
|
-
entries.push({ kind: 'header', category: cat });
|
|
409
|
-
for (const reg of filtered) {
|
|
410
|
-
entries.push({ kind: 'panel', reg });
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
this._cachedEntries = entries;
|
|
415
|
-
this._entriesDirty = false;
|
|
416
|
-
return entries;
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
/** Get the flat panel index (counting only panel entries) for a given panel id. */
|
|
420
|
-
private _flatPanelIndex(entries: ListEntry[], id: string): number {
|
|
421
|
-
let idx = 0;
|
|
422
|
-
for (const e of entries) {
|
|
423
|
-
if (e.kind !== 'panel') continue;
|
|
424
|
-
if (e.reg.id === id) return idx;
|
|
425
|
-
idx++;
|
|
426
|
-
}
|
|
427
|
-
return -1;
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
/** Get the ListEntry for the currently selected panel. */
|
|
431
|
-
private _getSelectedPanelEntry(entries: ListEntry[]): Extract<ListEntry, { kind: 'panel' }> | null {
|
|
432
|
-
let idx = 0;
|
|
433
|
-
for (const e of entries) {
|
|
434
|
-
if (e.kind !== 'panel') continue;
|
|
435
|
-
if (idx === this._selectedIndex) return e;
|
|
436
|
-
idx++;
|
|
437
|
-
}
|
|
438
|
-
return null;
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
private _setRouteMessage(reg: PanelRegistration): void {
|
|
442
|
-
const command = agentWorkspaceCommandForPanel(reg.id);
|
|
443
|
-
const area = agentWorkspaceCategoryForPanel(reg.id);
|
|
444
|
-
this._routeMessage = `${reg.name} routes through ${command} (${area}) in Agent workspace.`;
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
/** Move selection to the previous panel entry. */
|
|
448
|
-
private _movePrevPanel(): void {
|
|
449
|
-
this._selectedIndex = Math.max(0, this._selectedIndex - 1);
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
/** Move selection to the next panel entry. */
|
|
453
|
-
private _moveNextPanel(entries: ListEntry[]): void {
|
|
454
|
-
const panelCount = entries.filter(e => e.kind === 'panel').length;
|
|
455
|
-
this._selectedIndex = Math.min(panelCount - 1, this._selectedIndex + 1);
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
/** Clamp selectedIndex within available panels. */
|
|
459
|
-
private _clampSelection(entries: ListEntry[]): void {
|
|
460
|
-
const panelCount = entries.filter(e => e.kind === 'panel').length;
|
|
461
|
-
this._selectedIndex = Math.max(0, Math.min(this._selectedIndex, panelCount - 1));
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
}
|