@pellux/goodvibes-tui 0.28.0 → 1.0.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 +133 -0
- package/README.md +1 -1
- package/package.json +1 -1
- package/src/core/conversation-line-cache.ts +432 -0
- package/src/core/conversation-rendering.ts +11 -3
- package/src/core/conversation.ts +50 -4
- package/src/input/commands/cost-runtime.ts +49 -0
- package/src/input/commands/operator-runtime.ts +5 -1
- package/src/input/commands.ts +2 -0
- package/src/input/handler-content-actions.ts +8 -3
- package/src/input/handler-feed-routes.ts +12 -35
- package/src/input/handler-feed.ts +4 -8
- package/src/input/handler-shortcuts.ts +51 -0
- package/src/input/handler.ts +43 -6
- package/src/input/input-history.ts +17 -4
- package/src/input/keybindings.ts +48 -8
- package/src/input/panel-integration-actions.ts +107 -1
- package/src/main.ts +19 -11
- package/src/panels/agent-inspector-panel.ts +125 -36
- package/src/panels/agent-inspector-shared.ts +144 -0
- package/src/panels/approval-panel.ts +67 -16
- package/src/panels/automation-control-panel.ts +368 -124
- package/src/panels/base-panel.ts +1 -1
- package/src/panels/builtin/agent.ts +28 -37
- package/src/panels/builtin/development.ts +40 -32
- package/src/panels/builtin/knowledge.ts +15 -6
- package/src/panels/builtin/operations.ts +178 -117
- package/src/panels/builtin/session.ts +35 -9
- package/src/panels/builtin/shared.ts +98 -6
- package/src/panels/cockpit-panel.ts +232 -73
- package/src/panels/communication-panel.ts +58 -20
- package/src/panels/confirm-state.ts +8 -1
- package/src/panels/control-plane-panel.ts +351 -118
- package/src/panels/cost-tracker-panel.ts +165 -7
- package/src/panels/debug-panel.ts +312 -159
- package/src/panels/diff-panel.ts +209 -57
- package/src/panels/docs-panel.ts +152 -66
- package/src/panels/eval-panel.ts +220 -42
- package/src/panels/file-explorer-panel.ts +202 -122
- package/src/panels/file-preview-panel.ts +132 -57
- package/src/panels/git-panel.ts +310 -129
- package/src/panels/hooks-panel.ts +150 -22
- package/src/panels/incident-review-panel.ts +223 -35
- package/src/panels/index.ts +0 -4
- package/src/panels/intelligence-panel.ts +212 -86
- package/src/panels/knowledge-graph-panel.ts +461 -101
- package/src/panels/local-auth-panel.ts +240 -28
- package/src/panels/marketplace-panel.ts +193 -26
- package/src/panels/memory-panel.ts +78 -77
- package/src/panels/ops-control-panel.ts +146 -29
- package/src/panels/ops-strategy-panel.ts +72 -4
- package/src/panels/orchestration-panel.ts +231 -69
- package/src/panels/panel-list-panel.ts +154 -131
- package/src/panels/panel-manager.ts +97 -9
- package/src/panels/plan-dashboard-panel.ts +333 -17
- package/src/panels/plugins-panel.ts +184 -29
- package/src/panels/policy-panel.ts +210 -38
- package/src/panels/polish-core.ts +7 -2
- package/src/panels/polish.ts +23 -4
- package/src/panels/project-planning-answer-actions.ts +134 -0
- package/src/panels/project-planning-panel.ts +62 -113
- package/src/panels/provider-health-panel.ts +434 -518
- package/src/panels/provider-health-routes.ts +203 -0
- package/src/panels/provider-health-tracker.ts +194 -6
- package/src/panels/provider-health-views.ts +560 -0
- package/src/panels/qr-panel.ts +116 -30
- package/src/panels/remote-panel.ts +114 -36
- package/src/panels/routes-panel.ts +63 -22
- package/src/panels/sandbox-panel.ts +174 -42
- package/src/panels/scrollable-list-panel.ts +19 -135
- package/src/panels/security-panel.ts +133 -33
- package/src/panels/services-panel.ts +116 -64
- package/src/panels/session-browser-panel.ts +73 -2
- package/src/panels/session-maintenance.ts +4 -122
- package/src/panels/settings-sync-panel.ts +335 -72
- package/src/panels/skills-panel.ts +157 -89
- package/src/panels/subscription-panel.ts +86 -33
- package/src/panels/symbol-outline-panel.ts +248 -108
- package/src/panels/system-messages-panel.ts +114 -13
- package/src/panels/tasks-panel.ts +326 -139
- package/src/panels/thinking-panel.ts +43 -10
- package/src/panels/token-budget-panel.ts +194 -18
- package/src/panels/tool-inspector-panel.ts +144 -34
- package/src/panels/types.ts +40 -4
- package/src/panels/work-plan-panel.ts +280 -17
- package/src/panels/worktree-panel.ts +175 -42
- package/src/panels/wrfc-panel.ts +116 -24
- package/src/renderer/agent-detail-modal.ts +37 -8
- package/src/renderer/code-block.ts +58 -28
- package/src/renderer/context-inspector.ts +3 -6
- package/src/renderer/conversation-overlays.ts +25 -11
- package/src/renderer/conversation-surface.ts +1 -21
- package/src/renderer/diff-view.ts +6 -4
- package/src/renderer/footer-tips.ts +41 -0
- package/src/renderer/fullscreen-primitives.ts +23 -17
- package/src/renderer/fullscreen-workspace.ts +2 -7
- package/src/renderer/help-overlay.ts +91 -14
- package/src/renderer/hint-grammar.ts +52 -0
- package/src/renderer/history-search-overlay.ts +10 -16
- package/src/renderer/layout.ts +7 -7
- package/src/renderer/markdown.ts +9 -1
- package/src/renderer/modal-factory.ts +23 -15
- package/src/renderer/model-picker-overlay.ts +8 -485
- package/src/renderer/model-workspace.ts +9 -24
- package/src/renderer/overlay-box.ts +23 -19
- package/src/renderer/process-indicator.ts +15 -25
- package/src/renderer/profile-picker-modal.ts +13 -14
- package/src/renderer/prompt-content-width.ts +16 -0
- package/src/renderer/search-overlay.ts +27 -6
- package/src/renderer/selection-modal-overlay.ts +3 -1
- package/src/renderer/semantic-diff.ts +1 -1
- package/src/renderer/session-picker-modal.ts +6 -4
- package/src/renderer/settings-modal-helpers.ts +10 -34
- package/src/renderer/settings-modal.ts +70 -10
- package/src/renderer/shell-surface.ts +54 -15
- package/src/renderer/status-glyphs.ts +11 -9
- package/src/renderer/surface-layout.ts +0 -12
- package/src/renderer/term-caps.ts +1 -1
- package/src/renderer/theme.ts +60 -3
- package/src/renderer/tool-call.ts +6 -20
- package/src/renderer/ui-factory.ts +48 -37
- package/src/renderer/ui-primitives.ts +41 -3
- package/src/runtime/bootstrap-shell.ts +35 -18
- package/src/runtime/diagnostics/panels/index.ts +0 -3
- package/src/runtime/render-scheduler.ts +80 -0
- package/src/shell/ui-openers.ts +14 -22
- package/src/utils/format-duration.ts +2 -2
- package/src/utils/splash-lines.ts +54 -5
- package/src/version.ts +1 -1
- package/src/work-plans/work-plan-store.ts +13 -0
- package/src/panels/agent-logs-panel.ts +0 -635
- package/src/panels/agent-logs-shared.ts +0 -129
- package/src/panels/context-visualizer-panel.ts +0 -238
- package/src/panels/forensics-panel.ts +0 -378
- package/src/panels/provider-account-snapshot.ts +0 -259
- package/src/panels/provider-accounts-panel.ts +0 -255
- package/src/panels/provider-stats-panel.ts +0 -391
- package/src/panels/schedule-panel.ts +0 -365
- package/src/panels/watchers-panel.ts +0 -213
- package/src/renderer/file-tree.ts +0 -153
- package/src/renderer/progress.ts +0 -99
- package/src/renderer/status-token.ts +0 -67
- package/src/runtime/diagnostics/panels/panel-resources.ts +0 -118
- package/src/runtime/diagnostics/panels/policy.ts +0 -177
|
@@ -2,9 +2,11 @@ import type { Line } from '../types/grid.ts';
|
|
|
2
2
|
import { createEmptyLine } from '../types/grid.ts';
|
|
3
3
|
import { truncateDisplay } from '../utils/terminal-width.ts';
|
|
4
4
|
import { BasePanel } from './base-panel.ts';
|
|
5
|
+
import { FilePreviewPanel } from './file-preview-panel.ts';
|
|
6
|
+
import { SymbolOutlinePanel } from './symbol-outline-panel.ts';
|
|
7
|
+
import type { PanelIntegrationContext } from './types.ts';
|
|
5
8
|
import type { UiIntelligenceSnapshot, UiReadModel } from '../runtime/ui-read-models.ts';
|
|
6
9
|
import {
|
|
7
|
-
buildDetailBlock,
|
|
8
10
|
buildEmptyState,
|
|
9
11
|
buildGuidanceLine,
|
|
10
12
|
buildKeyboardHints,
|
|
@@ -12,19 +14,15 @@ import {
|
|
|
12
14
|
buildPanelLine,
|
|
13
15
|
buildPanelWorkspace,
|
|
14
16
|
buildStatusPill,
|
|
17
|
+
resolveStackedScrollableSections,
|
|
15
18
|
DEFAULT_PANEL_PALETTE,
|
|
16
19
|
type PanelWorkspaceSection,
|
|
17
20
|
type StyledPanelSegment,
|
|
18
21
|
} from './polish.ts';
|
|
19
22
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
warn: '#f59e0b',
|
|
24
|
-
bad: '#ef4444',
|
|
25
|
-
info: '#38bdf8',
|
|
26
|
-
headerBg: '#1e293b',
|
|
27
|
-
} as const;
|
|
23
|
+
// Base chrome only — state colors and text tokens come straight from
|
|
24
|
+
// DEFAULT_PANEL_PALETTE (WO-002).
|
|
25
|
+
const C = DEFAULT_PANEL_PALETTE;
|
|
28
26
|
|
|
29
27
|
interface DiagnosticFile {
|
|
30
28
|
readonly filePath: string;
|
|
@@ -50,6 +48,24 @@ export class IntelligencePanel extends BasePanel {
|
|
|
50
48
|
private readonly unsub: (() => void) | null;
|
|
51
49
|
private selectedIndex = 0;
|
|
52
50
|
|
|
51
|
+
/**
|
|
52
|
+
* The diagnostic file under the cursor. This panel owns its own selection
|
|
53
|
+
* state (`selectedIndex` navigates the `diagnosticFiles()` list directly), so
|
|
54
|
+
* every selected-row read routes through this one accessor — indexing the
|
|
55
|
+
* `diagnosticFiles()` list by the cursor directly is banned by the
|
|
56
|
+
* no-raw-selectedindex-read architecture rule.
|
|
57
|
+
*/
|
|
58
|
+
private selectedDiagnosticFile(): DiagnosticFile | undefined {
|
|
59
|
+
return this.diagnosticFiles().at(this.selectedIndex);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
private diagnosticsScrollOffset = 0;
|
|
63
|
+
private findingsScrollOffset = 0;
|
|
64
|
+
/** Set by handleInput('enter'); consumed by handlePanelIntegrationAction, which has the PanelManager reference needed to open the preview panel. */
|
|
65
|
+
private _pendingOpenFile: { filePath: string; line: number } | null = null;
|
|
66
|
+
/** Set by handleInput('s'); consumed by handlePanelIntegrationAction to open the file in preview AND sync the Symbols panel to it. */
|
|
67
|
+
private _pendingSymbolsPivot: { filePath: string; line: number } | null = null;
|
|
68
|
+
|
|
53
69
|
public constructor(private readonly readModel?: UiReadModel<UiIntelligenceSnapshot>) {
|
|
54
70
|
super('intelligence', 'Intelligence', 'J', 'development');
|
|
55
71
|
this.unsub = readModel ? readModel.subscribe(() => this.markDirty()) : null;
|
|
@@ -80,6 +96,12 @@ export class IntelligencePanel extends BasePanel {
|
|
|
80
96
|
.sort((a, b) => (b.errors - a.errors) || (b.warnings - a.warnings) || a.filePath.localeCompare(b.filePath));
|
|
81
97
|
}
|
|
82
98
|
|
|
99
|
+
/** The most actionable finding in a file — error-first, matching the file sort. */
|
|
100
|
+
private _firstFinding(file: DiagnosticFile): DiagnosticFile['diagnostics'][number] | undefined {
|
|
101
|
+
if (file.diagnostics.length === 0) return undefined;
|
|
102
|
+
return [...file.diagnostics].sort((a, b) => (a.severity === 'error' ? 0 : 1) - (b.severity === 'error' ? 0 : 1))[0];
|
|
103
|
+
}
|
|
104
|
+
|
|
83
105
|
public handleInput(key: string): boolean {
|
|
84
106
|
const files = this.diagnosticFiles();
|
|
85
107
|
if (files.length === 0) return false;
|
|
@@ -103,9 +125,74 @@ export class IntelligencePanel extends BasePanel {
|
|
|
103
125
|
this.markDirty();
|
|
104
126
|
return true;
|
|
105
127
|
}
|
|
128
|
+
if (key === 'enter' || key === 'return') {
|
|
129
|
+
const selected = this.selectedDiagnosticFile();
|
|
130
|
+
const first = selected ? this._firstFinding(selected) : undefined;
|
|
131
|
+
if (!selected || !first) return false;
|
|
132
|
+
this._pendingOpenFile = { filePath: selected.filePath, line: first.line + 1 };
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
if (key === 's') {
|
|
136
|
+
const selected = this.selectedDiagnosticFile();
|
|
137
|
+
const first = selected ? this._firstFinding(selected) : undefined;
|
|
138
|
+
if (!selected || !first) return false;
|
|
139
|
+
this._pendingSymbolsPivot = { filePath: selected.filePath, line: first.line + 1 };
|
|
140
|
+
return true;
|
|
141
|
+
}
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Cross-panel integration hook. Enter opens the selected file in the
|
|
147
|
+
* preview panel at its first (error-first) finding; 's' does the same and
|
|
148
|
+
* also syncs the Symbols panel to that file, pivoting straight from a
|
|
149
|
+
* diagnostic to its document symbols.
|
|
150
|
+
*/
|
|
151
|
+
handlePanelIntegrationAction(_key: string, ctx: PanelIntegrationContext): boolean {
|
|
152
|
+
if (this._pendingSymbolsPivot) {
|
|
153
|
+
const target = this._pendingSymbolsPivot;
|
|
154
|
+
this._pendingSymbolsPivot = null;
|
|
155
|
+
const previewPanel = this._openPreviewAt(ctx.panelManager, target.filePath, target.line);
|
|
156
|
+
if (!previewPanel) return false;
|
|
157
|
+
const symbols = ctx.panelManager.open('symbols');
|
|
158
|
+
if (symbols instanceof SymbolOutlinePanel) {
|
|
159
|
+
const source = previewPanel.getSource();
|
|
160
|
+
if (source !== null) symbols.loadFile(target.filePath, source);
|
|
161
|
+
}
|
|
162
|
+
return true;
|
|
163
|
+
}
|
|
164
|
+
if (this._pendingOpenFile) {
|
|
165
|
+
const target = this._pendingOpenFile;
|
|
166
|
+
this._pendingOpenFile = null;
|
|
167
|
+
return this._openPreviewAt(ctx.panelManager, target.filePath, target.line) !== null;
|
|
168
|
+
}
|
|
106
169
|
return false;
|
|
107
170
|
}
|
|
108
171
|
|
|
172
|
+
/** Open (or focus) the shared preview panel and jump it to filePath:line — same open/focus bridge FileExplorerPanel/DiffPanel use. */
|
|
173
|
+
private _openPreviewAt(panelManager: PanelIntegrationContext['panelManager'], filePath: string, line: number): FilePreviewPanel | null {
|
|
174
|
+
let previewPanel = panelManager.getPanel('preview');
|
|
175
|
+
if (previewPanel instanceof FilePreviewPanel) {
|
|
176
|
+
const pane = panelManager.getPaneOf('preview');
|
|
177
|
+
panelManager.activateById('preview');
|
|
178
|
+
if (pane) panelManager.focusPane(pane);
|
|
179
|
+
} else {
|
|
180
|
+
const targetPane: 'top' | 'bottom' = panelManager.isBottomPaneVisible()
|
|
181
|
+
? (panelManager.getFocusedPane() === 'top' ? 'bottom' : 'top')
|
|
182
|
+
: 'bottom';
|
|
183
|
+
const opened = panelManager.open('preview', targetPane);
|
|
184
|
+
panelManager.show();
|
|
185
|
+
panelManager.focusPane(targetPane);
|
|
186
|
+
previewPanel = opened instanceof FilePreviewPanel ? opened : null;
|
|
187
|
+
}
|
|
188
|
+
if (!(previewPanel instanceof FilePreviewPanel)) return null;
|
|
189
|
+
if (previewPanel.getCurrentFilePath() !== filePath) {
|
|
190
|
+
previewPanel.openFile(filePath);
|
|
191
|
+
}
|
|
192
|
+
previewPanel.goToLine(line);
|
|
193
|
+
return previewPanel;
|
|
194
|
+
}
|
|
195
|
+
|
|
109
196
|
public render(width: number, height: number): Line[] {
|
|
110
197
|
this.needsRender = false;
|
|
111
198
|
if (!this.readModel) {
|
|
@@ -137,38 +224,41 @@ export class IntelligencePanel extends BasePanel {
|
|
|
137
224
|
|
|
138
225
|
const diagnosticFiles = this.diagnosticFiles();
|
|
139
226
|
this.selectedIndex = Math.max(0, Math.min(this.selectedIndex, Math.max(0, diagnosticFiles.length - 1)));
|
|
227
|
+
const selectedFile = this.selectedDiagnosticFile();
|
|
228
|
+
const sortedFindings = selectedFile
|
|
229
|
+
? [...selectedFile.diagnostics].sort((a, b) => (a.severity === 'error' ? 0 : 1) - (b.severity === 'error' ? 0 : 1))
|
|
230
|
+
: [];
|
|
140
231
|
|
|
141
|
-
const
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
},
|
|
165
|
-
];
|
|
232
|
+
const postureSection: PanelWorkspaceSection = {
|
|
233
|
+
title: 'Intelligence posture',
|
|
234
|
+
lines: [
|
|
235
|
+
buildPanelLine(width, [
|
|
236
|
+
{ text: ' ', fg: C.dim },
|
|
237
|
+
...postureSegments('diagnostics', state.diagnosticsStatus),
|
|
238
|
+
{ text: ' ', fg: C.dim },
|
|
239
|
+
...postureSegments('symbols', state.symbolSearchStatus),
|
|
240
|
+
]),
|
|
241
|
+
buildPanelLine(width, [
|
|
242
|
+
{ text: ' ', fg: C.dim },
|
|
243
|
+
...postureSegments('completions', state.completionsStatus),
|
|
244
|
+
{ text: ' ', fg: C.dim },
|
|
245
|
+
...postureSegments('hover', state.hoverStatus),
|
|
246
|
+
]),
|
|
247
|
+
buildKeyValueLine(width, [
|
|
248
|
+
{ label: 'errors', value: String(state.errorCount), valueColor: state.errorCount > 0 ? C.bad : C.dim },
|
|
249
|
+
{ label: 'warnings', value: String(state.warningCount), valueColor: state.warningCount > 0 ? C.warn : C.dim },
|
|
250
|
+
{ label: 'requests', value: String(state.totalRequests), valueColor: C.value },
|
|
251
|
+
{ label: 'avg latency', value: `${Math.round(state.avgLatencyMs)}ms`, valueColor: C.info },
|
|
252
|
+
], C),
|
|
253
|
+
],
|
|
254
|
+
};
|
|
166
255
|
|
|
167
256
|
// Diagnostics — the most actionable surface. Render as a selectable list so
|
|
168
|
-
// the operator can drill into a specific file's findings.
|
|
169
|
-
|
|
257
|
+
// the operator can drill into a specific file's findings. Windowed via
|
|
258
|
+
// resolveStackedScrollableSections (WO-136) instead of a flat slice(0,6)
|
|
259
|
+
// so the cursor tracks into view instead of being able to scroll off-screen.
|
|
170
260
|
const diagnosticsLines: Line[] = diagnosticFiles.length > 0
|
|
171
|
-
?
|
|
261
|
+
? diagnosticFiles.map((entry, idx) => {
|
|
172
262
|
const selected = idx === this.selectedIndex;
|
|
173
263
|
const tone = entry.errors > 0 ? C.bad : entry.warnings > 0 ? C.warn : C.dim;
|
|
174
264
|
const counts = ` ${entry.errors} err ${entry.warnings} warn`;
|
|
@@ -180,70 +270,106 @@ export class IntelligencePanel extends BasePanel {
|
|
|
180
270
|
]);
|
|
181
271
|
})
|
|
182
272
|
: [buildPanelLine(width, [[' No tracked diagnostics. Symbol/diagnostic surfaces are clear.', C.dim]])];
|
|
183
|
-
if (diagnosticFiles.length > visibleFiles.length) {
|
|
184
|
-
diagnosticsLines.push(buildPanelLine(width, [[` +${diagnosticFiles.length - visibleFiles.length} more files`, C.dim]]));
|
|
185
|
-
}
|
|
186
|
-
sections.push({ title: `Diagnostics (${diagnosticFiles.length})`, lines: diagnosticsLines });
|
|
187
273
|
|
|
188
|
-
//
|
|
189
|
-
const
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
buildKeyValueLine(width, [
|
|
274
|
+
// Findings for the selected file — fully scrollable (WO-136: no 3-cap).
|
|
275
|
+
const findingsFixedLines: Line[] = selectedFile
|
|
276
|
+
? [buildKeyValueLine(width, [
|
|
277
|
+
{ label: 'file', value: truncateDisplay(selectedFile.filePath, Math.max(8, width - 34)), valueColor: C.value },
|
|
193
278
|
{ label: 'errors', value: String(selectedFile.errors), valueColor: selectedFile.errors > 0 ? C.bad : C.dim },
|
|
194
279
|
{ label: 'warnings', value: String(selectedFile.warnings), valueColor: selectedFile.warnings > 0 ? C.warn : C.dim },
|
|
195
|
-
], C)
|
|
196
|
-
];
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
detailRows.push(buildPanelLine(width, [[` +${selectedFile.diagnostics.length - topFindings.length} more findings`, C.dim]]));
|
|
211
|
-
}
|
|
212
|
-
detailRows.push(buildGuidanceLine(width, `/intelligence diagnostics ${selectedFile.filePath}`, 'open the full diagnostic list for this file', C));
|
|
213
|
-
sections.push({ lines: buildDetailBlock(width, truncateDisplay(selectedFile.filePath, Math.max(8, width - 4)), detailRows, C) });
|
|
214
|
-
}
|
|
280
|
+
], C)]
|
|
281
|
+
: [];
|
|
282
|
+
const findingsLines: Line[] = selectedFile
|
|
283
|
+
? (sortedFindings.length > 0
|
|
284
|
+
? sortedFindings.map((finding) => {
|
|
285
|
+
const loc = `${finding.line + 1}:${finding.column + 1}`;
|
|
286
|
+
const tag = finding.source ? `${finding.source}${finding.code ? `(${finding.code})` : ''}` : finding.severity;
|
|
287
|
+
const prefix = ` ${loc} ${tag}: `;
|
|
288
|
+
return buildPanelLine(width, [
|
|
289
|
+
[prefix, finding.severity === 'error' ? C.bad : C.warn],
|
|
290
|
+
[truncateDisplay(finding.message, Math.max(8, width - prefix.length - 1)), C.value],
|
|
291
|
+
]);
|
|
292
|
+
})
|
|
293
|
+
: [buildPanelLine(width, [[' No findings recorded for this file.', C.dim]])])
|
|
294
|
+
: [buildPanelLine(width, [[' Select a file above to inspect its findings.', C.dim]])];
|
|
215
295
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
buildGuidanceLine(
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
buildGuidanceLine(
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
296
|
+
// Recovery/Workflows collapse to a single contextual guidance line
|
|
297
|
+
// (WO-136) instead of a static wall of signposts.
|
|
298
|
+
const guidanceSection: PanelWorkspaceSection = degraded > 0
|
|
299
|
+
? {
|
|
300
|
+
title: 'Recovery',
|
|
301
|
+
lines: [buildGuidanceLine(
|
|
302
|
+
width,
|
|
303
|
+
'/intelligence repair',
|
|
304
|
+
`${degraded} of 4 surfaces degraded (diagnostics/symbols/hover/completions) — run repair`,
|
|
305
|
+
C,
|
|
306
|
+
)],
|
|
307
|
+
}
|
|
308
|
+
: {
|
|
309
|
+
title: 'Workflows',
|
|
310
|
+
lines: [buildGuidanceLine(
|
|
311
|
+
width,
|
|
312
|
+
'/intelligence symbols <file>',
|
|
313
|
+
'inspect document symbols for a file, or press s on a finding to pivot straight there',
|
|
314
|
+
C,
|
|
315
|
+
)],
|
|
316
|
+
};
|
|
235
317
|
|
|
236
318
|
const footerLines = diagnosticFiles.length > 0
|
|
237
319
|
? [buildKeyboardHints(width, [
|
|
238
320
|
{ keys: '↑/↓', label: 'select file' },
|
|
321
|
+
{ keys: 'Enter', label: 'open file at finding' },
|
|
322
|
+
{ keys: 's', label: 'pivot to symbols' },
|
|
239
323
|
{ keys: 'Home/End', label: 'jump' },
|
|
240
|
-
{ keys: '/intelligence', label: 'commands' },
|
|
241
324
|
], C)]
|
|
242
325
|
: [buildKeyboardHints(width, [
|
|
243
326
|
{ keys: '/intelligence diagnostics', label: 'review' },
|
|
244
327
|
{ keys: '/intelligence symbols <file>', label: 'inspect symbols' },
|
|
245
328
|
], C)];
|
|
246
329
|
|
|
330
|
+
const [diagnosticsWindow, findingsWindow] = resolveStackedScrollableSections(width, height, {
|
|
331
|
+
intro: 'Workspace intelligence posture across diagnostics, symbol search, hover, and completion readiness.',
|
|
332
|
+
footerLines,
|
|
333
|
+
palette: C,
|
|
334
|
+
beforeSections: [postureSection],
|
|
335
|
+
sections: [
|
|
336
|
+
{
|
|
337
|
+
title: `Diagnostics (${diagnosticFiles.length})`,
|
|
338
|
+
scrollableLines: diagnosticsLines,
|
|
339
|
+
selectedIndex: diagnosticFiles.length > 0 ? this.selectedIndex : undefined,
|
|
340
|
+
scrollOffset: this.diagnosticsScrollOffset,
|
|
341
|
+
minRows: 3,
|
|
342
|
+
weight: 2,
|
|
343
|
+
appendWindowSummary: diagnosticFiles.length > 0 ? {
|
|
344
|
+
dimColor: C.dim,
|
|
345
|
+
formatter: (window) => buildPanelLine(width, [[` showing ${window.start + 1}-${window.end} of ${window.total} files`, C.dim]]),
|
|
346
|
+
} : undefined,
|
|
347
|
+
},
|
|
348
|
+
{
|
|
349
|
+
title: selectedFile ? `Findings — ${truncateDisplay(selectedFile.filePath, Math.max(8, width - 14))}` : 'Findings',
|
|
350
|
+
fixedLines: findingsFixedLines,
|
|
351
|
+
scrollableLines: findingsLines,
|
|
352
|
+
scrollOffset: this.findingsScrollOffset,
|
|
353
|
+
minRows: 2,
|
|
354
|
+
weight: 1,
|
|
355
|
+
appendWindowSummary: sortedFindings.length > 0 ? {
|
|
356
|
+
dimColor: C.dim,
|
|
357
|
+
formatter: (window) => buildPanelLine(width, [[` showing ${window.start + 1}-${window.end} of ${window.total} findings`, C.dim]]),
|
|
358
|
+
} : undefined,
|
|
359
|
+
},
|
|
360
|
+
],
|
|
361
|
+
afterSections: [guidanceSection],
|
|
362
|
+
});
|
|
363
|
+
this.diagnosticsScrollOffset = diagnosticsWindow?.scrollOffset ?? this.diagnosticsScrollOffset;
|
|
364
|
+
this.findingsScrollOffset = findingsWindow?.scrollOffset ?? this.findingsScrollOffset;
|
|
365
|
+
|
|
366
|
+
const sections: PanelWorkspaceSection[] = [
|
|
367
|
+
postureSection,
|
|
368
|
+
diagnosticsWindow?.section ?? { title: `Diagnostics (${diagnosticFiles.length})`, lines: diagnosticsLines },
|
|
369
|
+
findingsWindow?.section ?? { title: 'Findings', lines: [...findingsFixedLines, ...findingsLines] },
|
|
370
|
+
guidanceSection,
|
|
371
|
+
];
|
|
372
|
+
|
|
247
373
|
const lines = buildPanelWorkspace(width, height, {
|
|
248
374
|
title: 'Intelligence Control Room',
|
|
249
375
|
intro: 'Workspace intelligence posture across diagnostics, symbol search, hover, and completion readiness.',
|