@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
package/src/panels/diff-panel.ts
CHANGED
|
@@ -6,6 +6,10 @@ import type { Line } from '../types/grid.ts';
|
|
|
6
6
|
import { createStyledCell, createEmptyLine } from '../types/grid.ts';
|
|
7
7
|
import { truncateDisplay, getDisplayWidth } from '../utils/terminal-width.ts';
|
|
8
8
|
import { BasePanel } from './base-panel.ts';
|
|
9
|
+
import { UI_TONES, DIFF_TONES } from '../renderer/ui-primitives.ts';
|
|
10
|
+
import { FilePreviewPanel } from './file-preview-panel.ts';
|
|
11
|
+
import type { PanelIntegrationContext } from './types.ts';
|
|
12
|
+
import { logger } from '@pellux/goodvibes-sdk/platform/utils';
|
|
9
13
|
import {
|
|
10
14
|
buildBodyText,
|
|
11
15
|
buildEmptyState,
|
|
@@ -14,31 +18,69 @@ import {
|
|
|
14
18
|
buildStyledPanelLine,
|
|
15
19
|
type PanelWorkspaceSection,
|
|
16
20
|
DEFAULT_PANEL_PALETTE,
|
|
21
|
+
extendPalette,
|
|
17
22
|
} from './polish.ts';
|
|
18
23
|
|
|
19
24
|
// ---------------------------------------------------------------------------
|
|
20
|
-
// Colour palette
|
|
25
|
+
// Colour palette — dedicated diff-viewer scheme (like a mini syntax
|
|
26
|
+
// highlighter). Each hex literal is named once and reused for every role
|
|
27
|
+
// that shares it, including the workspace-chrome aliases (info/dim/value/
|
|
28
|
+
// empty) so the raw-hex count never grows. The title band itself is NOT
|
|
29
|
+
// overridden — buildPanelWorkspace always falls back to the canonical
|
|
30
|
+
// DEFAULT_PANEL_PALETTE.headerBg (WO-002: one title band everywhere).
|
|
21
31
|
// ---------------------------------------------------------------------------
|
|
22
32
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
33
|
+
// Hunk blue is the shared DIFF_TONES token (WO-204) — diff-view.ts (conversation)
|
|
34
|
+
// and git-panel.ts's inline diff converge onto this file's pre-existing value.
|
|
35
|
+
const HUNK_BLUE: string = DIFF_TONES.hunk;
|
|
36
|
+
// Context rows and line-number gutter use the shared theme's muted/dim
|
|
37
|
+
// foreground tones rather than dedicated gray hex literals.
|
|
38
|
+
const CONTEXT_GRAY = UI_TONES.fg.muted;
|
|
39
|
+
const FILENAME_WHITE = '#ffffff';
|
|
40
|
+
// Add/del text colors are the shared DIFF_TONES tokens, whose values ARE this
|
|
41
|
+
// panel's shipped colors — this panel is the reference diff look (diff-view
|
|
42
|
+
// was unwired dead code until WO-204, so "majority of surfaces" was a mirage);
|
|
43
|
+
// the conversation surface converges onto these, not the reverse.
|
|
44
|
+
const ADD_GREEN: string = DIFF_TONES.add;
|
|
45
|
+
const ADD_BG = '#001a0d';
|
|
46
|
+
const DEL_RED: string = DIFF_TONES.del;
|
|
47
|
+
const DEL_BG = '#1a0000';
|
|
48
|
+
const HUNK_BG = '#0a0a1a';
|
|
49
|
+
const MARKER_GRAY = '#aaaaaa';
|
|
50
|
+
const LINE_NUM_GRAY = UI_TONES.fg.dim;
|
|
51
|
+
const LINE_NUM_ADD = '#00aa55';
|
|
52
|
+
const LINE_NUM_DEL = '#aa2222';
|
|
53
|
+
const TAB_ACTIVE_BG = '#333333';
|
|
54
|
+
const TAB_INACTIVE_GRAY = '#666666';
|
|
55
|
+
const TAB_BG = '#222222';
|
|
56
|
+
const STATUS_BAR_BG = '#444444';
|
|
57
|
+
|
|
58
|
+
const COLOR = extendPalette(DEFAULT_PANEL_PALETTE, {
|
|
59
|
+
// Workspace-chrome aliases (title band excluded — no headerBg override)
|
|
60
|
+
info: HUNK_BLUE,
|
|
61
|
+
dim: CONTEXT_GRAY,
|
|
62
|
+
value: FILENAME_WHITE,
|
|
63
|
+
empty: CONTEXT_GRAY,
|
|
64
|
+
|
|
65
|
+
// Domain accents
|
|
66
|
+
addition: ADD_GREEN,
|
|
67
|
+
additionBg: ADD_BG,
|
|
68
|
+
deletion: DEL_RED,
|
|
69
|
+
deletionBg: DEL_BG,
|
|
70
|
+
hunk: HUNK_BLUE,
|
|
71
|
+
hunkBg: HUNK_BG,
|
|
72
|
+
markerText: MARKER_GRAY,
|
|
73
|
+
lineNum: LINE_NUM_GRAY,
|
|
74
|
+
lineNumAdd: LINE_NUM_ADD,
|
|
75
|
+
lineNumDel: LINE_NUM_DEL,
|
|
76
|
+
filename: FILENAME_WHITE,
|
|
77
|
+
tabActive: FILENAME_WHITE,
|
|
78
|
+
tabActiveBg: TAB_ACTIVE_BG,
|
|
79
|
+
tabInactive: TAB_INACTIVE_GRAY,
|
|
80
|
+
tabBg: TAB_BG,
|
|
81
|
+
context: CONTEXT_GRAY,
|
|
82
|
+
statusBar: STATUS_BAR_BG,
|
|
83
|
+
} as const);
|
|
42
84
|
|
|
43
85
|
// ---------------------------------------------------------------------------
|
|
44
86
|
// Types
|
|
@@ -190,6 +232,9 @@ export class DiffPanel extends BasePanel {
|
|
|
190
232
|
private selectedFile = 0;
|
|
191
233
|
private scrollOffset = 0;
|
|
192
234
|
|
|
235
|
+
/** Set by the 'o' key; consumed by handlePanelIntegrationAction on the next dispatch. */
|
|
236
|
+
private pendingOpenPreview = false;
|
|
237
|
+
|
|
193
238
|
constructor(workingDirectory: string) {
|
|
194
239
|
super('diff', 'Diff', 'D', 'development');
|
|
195
240
|
this.workingDirectory = workingDirectory;
|
|
@@ -233,9 +278,14 @@ export class DiffPanel extends BasePanel {
|
|
|
233
278
|
const raw = await new Response(proc.stdout).text();
|
|
234
279
|
await proc.exited;
|
|
235
280
|
this.loadRawDiff(raw);
|
|
281
|
+
this.enrichSemanticDiffs(files, ref ?? 'HEAD').catch((err) => { logger.debug('DiffPanel: semantic diff enrichment failed', { err }); });
|
|
236
282
|
}
|
|
237
283
|
|
|
238
|
-
/**
|
|
284
|
+
/**
|
|
285
|
+
* Run `git diff` and populate all changed files. Self-load entry point for
|
|
286
|
+
* the 'w' (working tree, no ref) and 'h' (vs HEAD) keys. Automatically
|
|
287
|
+
* enriches every loaded file with a semantic-diff summary in the background.
|
|
288
|
+
*/
|
|
239
289
|
async showGitDiff(ref?: string): Promise<void> {
|
|
240
290
|
const args = ['diff', ...(ref ? [ref] : [])];
|
|
241
291
|
const proc = Bun.spawn(['git', ...args], { stdout: 'pipe', stderr: 'pipe', cwd: this.workingDirectory });
|
|
@@ -266,6 +316,73 @@ export class DiffPanel extends BasePanel {
|
|
|
266
316
|
this.selectedFile = Math.min(this.selectedFile, Math.max(0, this.entries.length - 1));
|
|
267
317
|
this.scrollOffset = 0;
|
|
268
318
|
this.markDirty();
|
|
319
|
+
this.enrichSemanticDiffs(newEntries.map((e) => e.filePath), 'HEAD').catch((err) => { logger.debug('DiffPanel: semantic diff enrichment failed', { err }); });
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Run `git diff --cached` and populate the staged changes. Self-load entry
|
|
324
|
+
* point for the 's' key — its own diff plumbing, independent of the
|
|
325
|
+
* `/diff staged` command handler.
|
|
326
|
+
*/
|
|
327
|
+
async showStagedDiff(): Promise<void> {
|
|
328
|
+
const proc = Bun.spawn(['/bin/sh', '-c', 'git diff --cached'], { stdout: 'pipe', stderr: 'pipe', cwd: this.workingDirectory });
|
|
329
|
+
const [raw, errText] = await Promise.all([
|
|
330
|
+
new Response(proc.stdout).text(),
|
|
331
|
+
new Response(proc.stderr).text(),
|
|
332
|
+
]);
|
|
333
|
+
const exitCode = await proc.exited;
|
|
334
|
+
if (exitCode !== 0) {
|
|
335
|
+
const errorText = errText.trim() || 'git diff --cached failed';
|
|
336
|
+
this.showDiff('(error)', `--- error\n+++ error\n@@ -0,0 +1,1 @@\n+${errorText}`);
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
if (!raw.trim()) {
|
|
340
|
+
this.showDiff('(no staged changes)', '@@ -0,0 +0,0 @@\n No staged changes.');
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
this.loadRawDiff(raw);
|
|
344
|
+
this.enrichSemanticDiffs(this.entries.map((e) => e.filePath), 'HEAD').catch((err) => { logger.debug('DiffPanel: semantic diff enrichment failed', { err }); });
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Best-effort semantic-diff enrichment for a set of files, run against the
|
|
349
|
+
* before-content at `ref` and the current on-disk after-content. Self-contained
|
|
350
|
+
* diff plumbing — does not depend on the `/diff` command handler.
|
|
351
|
+
*/
|
|
352
|
+
private async enrichSemanticDiffs(files: string[], ref: string): Promise<void> {
|
|
353
|
+
if (files.length === 0) return;
|
|
354
|
+
const { computeSemanticDiff, formatSemanticDiffSummary } = await import('../renderer/semantic-diff.ts');
|
|
355
|
+
const { join, relative: pathRelative } = await import('path');
|
|
356
|
+
const repoRootProc = Bun.spawn(['git', 'rev-parse', '--show-toplevel'], { stdout: 'pipe', cwd: this.workingDirectory });
|
|
357
|
+
await repoRootProc.exited;
|
|
358
|
+
const repoRoot = (await new Response(repoRootProc.stdout).text()).trim() || this.workingDirectory;
|
|
359
|
+
await Promise.allSettled(
|
|
360
|
+
files.map(async (filePath) => {
|
|
361
|
+
try {
|
|
362
|
+
const absPath = filePath.startsWith('/') ? filePath : join(this.workingDirectory, filePath);
|
|
363
|
+
const repoRelPath = filePath.startsWith('/') ? pathRelative(repoRoot, filePath) : filePath;
|
|
364
|
+
const [beforeResult, afterResult] = await Promise.allSettled([
|
|
365
|
+
(async () => {
|
|
366
|
+
const proc = Bun.spawn(
|
|
367
|
+
['git', 'show', `${ref}:${repoRelPath}`],
|
|
368
|
+
{ stdout: 'pipe', stderr: 'pipe', cwd: repoRoot },
|
|
369
|
+
);
|
|
370
|
+
const [text, exitCode] = await Promise.all([new Response(proc.stdout).text(), proc.exited]);
|
|
371
|
+
if (exitCode !== 0) throw new Error(`git show failed for ${repoRelPath}`);
|
|
372
|
+
return text;
|
|
373
|
+
})(),
|
|
374
|
+
Bun.file(absPath).text(),
|
|
375
|
+
]);
|
|
376
|
+
if (beforeResult.status !== 'fulfilled' || afterResult.status !== 'fulfilled') return;
|
|
377
|
+
const semanticDiff = await computeSemanticDiff(filePath, beforeResult.value, afterResult.value);
|
|
378
|
+
if (!semanticDiff) return;
|
|
379
|
+
const summary = formatSemanticDiffSummary(semanticDiff);
|
|
380
|
+
if (summary) this.setSemanticSummary(filePath, summary);
|
|
381
|
+
} catch {
|
|
382
|
+
// best-effort only
|
|
383
|
+
}
|
|
384
|
+
}),
|
|
385
|
+
);
|
|
269
386
|
}
|
|
270
387
|
|
|
271
388
|
/**
|
|
@@ -306,12 +423,62 @@ export class DiffPanel extends BasePanel {
|
|
|
306
423
|
case 'up': this.scrollUp(); return true;
|
|
307
424
|
case 'down': this.scrollDown(); return true;
|
|
308
425
|
case 'tab': this.nextFile(); return true;
|
|
426
|
+
// Shift+Tab: most terminals send the bare CSI-Z ("backtab") escape
|
|
427
|
+
// sequence rather than a Tab keypress with a shift modifier, and the
|
|
428
|
+
// input tokenizer passes that sequence through unchanged as the
|
|
429
|
+
// logical key name — so it (and the friendlier aliases some terminals/
|
|
430
|
+
// future tokenizer versions may emit) are matched explicitly here.
|
|
431
|
+
case '\x1b[Z':
|
|
432
|
+
case 'shift-tab':
|
|
433
|
+
case 'backtab': this.prevFile(); return true;
|
|
309
434
|
case 'pageup': this.scrollPageUp(); return true;
|
|
310
435
|
case 'pagedown': this.scrollPageDown(); return true;
|
|
436
|
+
case 'w': void this.showGitDiff(); return true;
|
|
437
|
+
case 'h': void this.showGitDiff('HEAD'); return true;
|
|
438
|
+
case 's': void this.showStagedDiff(); return true;
|
|
439
|
+
case 'o': {
|
|
440
|
+
if (!this.currentEntry()) return false;
|
|
441
|
+
this.pendingOpenPreview = true;
|
|
442
|
+
return true;
|
|
443
|
+
}
|
|
311
444
|
default: return false;
|
|
312
445
|
}
|
|
313
446
|
}
|
|
314
447
|
|
|
448
|
+
/**
|
|
449
|
+
* Cross-panel integration hook — 'o' opens the currently selected file in
|
|
450
|
+
* the preview panel via the same open/focus bridge FileExplorerPanel uses
|
|
451
|
+
* (src/input/panel-integration-actions.ts), without this panel needing to
|
|
452
|
+
* know about PanelManager pane/focus mechanics beyond what ctx exposes.
|
|
453
|
+
*/
|
|
454
|
+
handlePanelIntegrationAction(_key: string, ctx: PanelIntegrationContext): boolean {
|
|
455
|
+
if (!this.pendingOpenPreview) return false;
|
|
456
|
+
this.pendingOpenPreview = false;
|
|
457
|
+
const entry = this.currentEntry();
|
|
458
|
+
if (!entry) return false;
|
|
459
|
+
|
|
460
|
+
const pm = ctx.panelManager;
|
|
461
|
+
let previewPanel = pm.getPanel('preview');
|
|
462
|
+
if (previewPanel instanceof FilePreviewPanel) {
|
|
463
|
+
const pane = pm.getPaneOf('preview');
|
|
464
|
+
pm.activateById('preview');
|
|
465
|
+
if (pane) pm.focusPane(pane);
|
|
466
|
+
} else {
|
|
467
|
+
const targetPane: 'top' | 'bottom' = pm.isBottomPaneVisible()
|
|
468
|
+
? (pm.getFocusedPane() === 'top' ? 'bottom' : 'top')
|
|
469
|
+
: 'bottom';
|
|
470
|
+
const opened = pm.open('preview', targetPane);
|
|
471
|
+
pm.show();
|
|
472
|
+
pm.focusPane(targetPane);
|
|
473
|
+
previewPanel = opened instanceof FilePreviewPanel ? opened : null;
|
|
474
|
+
}
|
|
475
|
+
if (previewPanel instanceof FilePreviewPanel) {
|
|
476
|
+
previewPanel.openFile(entry.filePath);
|
|
477
|
+
return true;
|
|
478
|
+
}
|
|
479
|
+
return false;
|
|
480
|
+
}
|
|
481
|
+
|
|
315
482
|
private scrollUp(): void {
|
|
316
483
|
if (this.scrollOffset > 0) {
|
|
317
484
|
this.scrollOffset--;
|
|
@@ -349,6 +516,13 @@ export class DiffPanel extends BasePanel {
|
|
|
349
516
|
this.markDirty();
|
|
350
517
|
}
|
|
351
518
|
|
|
519
|
+
private prevFile(): void {
|
|
520
|
+
if (this.entries.length === 0) return;
|
|
521
|
+
this.selectedFile = (this.selectedFile - 1 + this.entries.length) % this.entries.length;
|
|
522
|
+
this.scrollOffset = 0;
|
|
523
|
+
this.markDirty();
|
|
524
|
+
}
|
|
525
|
+
|
|
352
526
|
private currentEntry(): DiffEntry | null {
|
|
353
527
|
return this.entries[this.selectedFile] ?? null;
|
|
354
528
|
}
|
|
@@ -364,26 +538,20 @@ export class DiffPanel extends BasePanel {
|
|
|
364
538
|
if (this.entries.length === 0) {
|
|
365
539
|
return buildPanelWorkspace(width, height, {
|
|
366
540
|
title: 'Diff Workspace',
|
|
367
|
-
palette:
|
|
368
|
-
...DEFAULT_PANEL_PALETTE,
|
|
369
|
-
info: COLOR.hunk,
|
|
370
|
-
dim: COLOR.context,
|
|
371
|
-
value: COLOR.filename,
|
|
372
|
-
},
|
|
541
|
+
palette: COLOR,
|
|
373
542
|
sections: [{
|
|
374
543
|
title: 'Diff',
|
|
375
544
|
lines: buildEmptyState(
|
|
376
545
|
width,
|
|
377
546
|
' No diff to display.',
|
|
378
|
-
'Load a git diff or select a changed file to populate the workspace.',
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
},
|
|
547
|
+
'Load a git diff or select a changed file to populate the workspace. w=working h=HEAD s=staged self-load right here.',
|
|
548
|
+
// WO-160: corrected — bare /diff loads files changed *this
|
|
549
|
+
// session* (falling back to the full HEAD diff when none are
|
|
550
|
+
// tracked yet), not the working-tree diff the w key already
|
|
551
|
+
// loads in-panel; the old summary text described the wrong
|
|
552
|
+
// command.
|
|
553
|
+
[{ command: '/diff', summary: 'load files changed this session into the diff workspace (falls back to the HEAD diff if none yet)' }],
|
|
554
|
+
COLOR,
|
|
387
555
|
),
|
|
388
556
|
}],
|
|
389
557
|
});
|
|
@@ -396,20 +564,10 @@ export class DiffPanel extends BasePanel {
|
|
|
396
564
|
|
|
397
565
|
const compact = height <= 12;
|
|
398
566
|
const summaryLines = entry.semanticSummary
|
|
399
|
-
? buildBodyText(width, `Semantic summary: ${entry.semanticSummary}`,
|
|
400
|
-
...DEFAULT_PANEL_PALETTE,
|
|
401
|
-
dim: COLOR.context,
|
|
402
|
-
value: COLOR.filename,
|
|
403
|
-
}, COLOR.context)
|
|
567
|
+
? buildBodyText(width, `Semantic summary: ${entry.semanticSummary}`, COLOR, COLOR.context)
|
|
404
568
|
: [];
|
|
405
569
|
const previewSection = resolveScrollablePanelSection(width, height, {
|
|
406
|
-
palette:
|
|
407
|
-
...DEFAULT_PANEL_PALETTE,
|
|
408
|
-
info: COLOR.hunk,
|
|
409
|
-
dim: COLOR.context,
|
|
410
|
-
value: COLOR.filename,
|
|
411
|
-
headerBg: COLOR.tabBg,
|
|
412
|
-
},
|
|
570
|
+
palette: COLOR,
|
|
413
571
|
footerLines: [this.renderStatusBar(width, entry)],
|
|
414
572
|
beforeSections: [
|
|
415
573
|
{
|
|
@@ -444,13 +602,7 @@ export class DiffPanel extends BasePanel {
|
|
|
444
602
|
];
|
|
445
603
|
return buildPanelWorkspace(width, height, {
|
|
446
604
|
title: 'Diff Workspace',
|
|
447
|
-
palette:
|
|
448
|
-
...DEFAULT_PANEL_PALETTE,
|
|
449
|
-
info: COLOR.hunk,
|
|
450
|
-
dim: COLOR.context,
|
|
451
|
-
value: COLOR.filename,
|
|
452
|
-
headerBg: COLOR.tabBg,
|
|
453
|
-
},
|
|
605
|
+
palette: COLOR,
|
|
454
606
|
sections,
|
|
455
607
|
footerLines: [this.renderStatusBar(width, entry)],
|
|
456
608
|
});
|
|
@@ -509,7 +661,7 @@ export class DiffPanel extends BasePanel {
|
|
|
509
661
|
{ text: ' -', fg: COLOR.tabInactive, bg: COLOR.statusBar },
|
|
510
662
|
{ text: String(stat.removed), fg: COLOR.deletion, bg: COLOR.statusBar },
|
|
511
663
|
{ text: ` L${this.scrollOffset + 1}/${entry.lines.length}`, fg: COLOR.tabInactive, bg: COLOR.statusBar },
|
|
512
|
-
{ text: ' Tab
|
|
664
|
+
{ text: ' Tab/S-Tab file o open w/h/s load ↑/↓ scroll', fg: COLOR.context, bg: COLOR.statusBar },
|
|
513
665
|
];
|
|
514
666
|
if (entry.semanticSummary) {
|
|
515
667
|
segments.push({ text: ` ◈ ${entry.semanticSummary}`, fg: COLOR.hunk, bg: COLOR.statusBar });
|
|
@@ -531,7 +683,7 @@ export class DiffPanel extends BasePanel {
|
|
|
531
683
|
case 'hunk':
|
|
532
684
|
return renderText(width, pl.text, COLOR.hunk, COLOR.hunkBg, false);
|
|
533
685
|
case 'header':
|
|
534
|
-
return renderText(width, pl.text, COLOR.
|
|
686
|
+
return renderText(width, pl.text, COLOR.markerText, '', false);
|
|
535
687
|
case 'context':
|
|
536
688
|
default:
|
|
537
689
|
return makeLine(width, left, right, ` ${pl.text}`, COLOR.context, '', COLOR.lineNum, false);
|