@pellux/goodvibes-tui 0.18.20 → 0.19.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 +154 -0
- package/README.md +1 -1
- package/docs/foundation-artifacts/operator-contract.json +1 -1
- package/package.json +7 -3
- package/src/core/conversation-rendering.ts +22 -6
- package/src/core/orchestrator.ts +1 -1
- package/src/input/commands/diff-runtime.ts +6 -5
- package/src/input/commands/guidance-runtime.ts +1 -1
- package/src/input/commands/health-runtime.ts +2 -2
- package/src/input/commands/local-setup-review.ts +1 -1
- package/src/input/commands/session-content.ts +1 -1
- package/src/input/commands/session.ts +0 -1
- package/src/input/commands/shell-core.ts +3 -2
- package/src/input/commands/skills-runtime.ts +2 -2
- package/src/input/commands/subscription-runtime.ts +4 -4
- package/src/input/feed-context-factory.ts +236 -0
- package/src/input/handler-feed.ts +44 -6
- package/src/input/handler-shortcuts.ts +138 -125
- package/src/input/handler.ts +119 -119
- package/src/input/keybindings.ts +30 -0
- package/src/input/panel-integration-actions.ts +2 -1
- package/src/input/settings-modal-types.ts +60 -0
- package/src/input/settings-modal.ts +83 -65
- package/src/panels/agent-inspector-panel.ts +10 -9
- package/src/panels/agent-logs-panel.ts +26 -6
- package/src/panels/approval-panel.ts +55 -82
- package/src/panels/automation-control-panel.ts +120 -161
- package/src/panels/base-panel.ts +108 -3
- package/src/panels/communication-panel.ts +69 -107
- package/src/panels/context-visualizer-panel.ts +2 -0
- package/src/panels/control-plane-panel.ts +117 -172
- package/src/panels/diff-panel.ts +2 -0
- package/src/panels/file-explorer-panel.ts +51 -31
- package/src/panels/file-preview-panel.ts +57 -35
- package/src/panels/git-panel.ts +12 -13
- package/src/panels/hooks-panel.ts +103 -138
- package/src/panels/incident-review-panel.ts +59 -109
- package/src/panels/knowledge-panel.ts +75 -107
- package/src/panels/local-auth-panel.ts +77 -93
- package/src/panels/marketplace-panel.ts +51 -69
- package/src/panels/mcp-panel.ts +110 -155
- package/src/panels/memory-panel.ts +90 -158
- package/src/panels/ops-control-panel.ts +51 -85
- package/src/panels/orchestration-panel.ts +70 -51
- package/src/panels/panel-list-panel.ts +5 -4
- package/src/panels/panel-manager.ts +25 -2
- package/src/panels/plan-dashboard-panel.ts +2 -0
- package/src/panels/plugins-panel.ts +37 -60
- package/src/panels/polish.ts +51 -2
- package/src/panels/provider-accounts-panel.ts +1 -0
- package/src/panels/provider-health-panel.ts +6 -8
- package/src/panels/routes-panel.ts +91 -141
- package/src/panels/schedule-panel.ts +7 -6
- package/src/panels/scrollable-list-panel.ts +64 -16
- package/src/panels/security-panel.ts +118 -152
- package/src/panels/services-panel.ts +63 -105
- package/src/panels/session-browser-panel.ts +19 -18
- package/src/panels/settings-sync-panel.ts +79 -123
- package/src/panels/skills-panel.ts +114 -230
- package/src/panels/subscription-panel.ts +64 -86
- package/src/panels/system-messages-panel.ts +147 -141
- package/src/panels/tasks-panel.ts +130 -179
- package/src/panels/token-budget-panel.ts +2 -0
- package/src/panels/watchers-panel.ts +89 -137
- package/src/panels/worktree-panel.ts +1 -0
- package/src/panels/wrfc-panel.ts +2 -0
- package/src/renderer/agent-detail-modal.ts +2 -2
- package/src/renderer/ansi-sanitize.ts +76 -0
- package/src/renderer/buffer.ts +23 -1
- package/src/renderer/diff.ts +8 -0
- package/src/renderer/help-overlay.ts +48 -28
- package/src/renderer/markdown.ts +3 -145
- package/src/renderer/settings-modal-helpers.ts +27 -0
- package/src/renderer/settings-modal.ts +18 -1
- package/src/renderer/status-glyphs.ts +21 -0
- package/src/renderer/status-token.ts +4 -8
- package/src/renderer/tool-call.ts +4 -3
- package/src/runtime/bootstrap-core.ts +1 -1
- package/src/runtime/bootstrap-hook-bridge.ts +1 -1
- package/src/runtime/bootstrap.ts +7 -8
- package/src/runtime/diagnostics/panels/policy.ts +2 -1
- package/src/shell/ui-openers.ts +1 -1
- package/src/version.ts +1 -1
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
isPanelSearchCommit,
|
|
27
27
|
isPanelSearchPrintable,
|
|
28
28
|
} from './search-focus.ts';
|
|
29
|
+
import { type ConfirmState, handleConfirmInput } from './confirm-state.ts';
|
|
29
30
|
|
|
30
31
|
const C = {
|
|
31
32
|
headerBg: '#1a1a2e',
|
|
@@ -79,7 +80,7 @@ function formatReturnContextLines(returnContext: SessionInfo['returnContext']):
|
|
|
79
80
|
// ---------------------------------------------------------------------------
|
|
80
81
|
// Confirmation state for deletion
|
|
81
82
|
// ---------------------------------------------------------------------------
|
|
82
|
-
|
|
83
|
+
// ConfirmState<string> — subject holds the session name to delete
|
|
83
84
|
|
|
84
85
|
export class SessionBrowserPanel extends BasePanel {
|
|
85
86
|
private sessions: SessionInfo[] = [];
|
|
@@ -88,10 +89,10 @@ export class SessionBrowserPanel extends BasePanel {
|
|
|
88
89
|
private searching = false; // true when user is actively typing a search
|
|
89
90
|
private cursorIndex = 0;
|
|
90
91
|
private scrollOffset = 0;
|
|
91
|
-
private confirm: ConfirmState = null;
|
|
92
|
+
private confirm: ConfirmState<string> | null = null;
|
|
92
93
|
private deleteError = '';
|
|
93
94
|
private loadError = '';
|
|
94
|
-
private
|
|
95
|
+
private refreshTimerId: ReturnType<typeof setInterval> | null = null;
|
|
95
96
|
|
|
96
97
|
constructor(
|
|
97
98
|
private readonly sessionManager: SessionBrowserQuery,
|
|
@@ -103,29 +104,29 @@ export class SessionBrowserPanel extends BasePanel {
|
|
|
103
104
|
override onActivate(): void {
|
|
104
105
|
super.onActivate();
|
|
105
106
|
this._load();
|
|
106
|
-
this.
|
|
107
|
+
this.refreshTimerId = this.registerTimer(setInterval(() => { this._load(); }, 5000));
|
|
107
108
|
}
|
|
108
109
|
|
|
109
110
|
override onDeactivate(): void {
|
|
110
|
-
if (this.
|
|
111
|
+
if (this.refreshTimerId !== null) { this.clearTimer(this.refreshTimerId); this.refreshTimerId = null; }
|
|
111
112
|
this.searching = false;
|
|
112
113
|
this.confirm = null;
|
|
113
114
|
super.onDeactivate();
|
|
114
115
|
}
|
|
115
116
|
|
|
116
117
|
handleInput(key: string): boolean {
|
|
117
|
-
// Confirmation dialog
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
}
|
|
118
|
+
// Confirmation dialog — use shared handleConfirmInput for y/n/Esc UX
|
|
119
|
+
const confirmResult = handleConfirmInput(this.confirm, key);
|
|
120
|
+
if (confirmResult === 'confirmed') {
|
|
121
|
+
this._deleteConfirmed();
|
|
122
|
+
return true;
|
|
123
|
+
}
|
|
124
|
+
if (confirmResult === 'cancelled') {
|
|
125
|
+
this.confirm = null;
|
|
126
|
+
this.markDirty();
|
|
127
127
|
return true;
|
|
128
128
|
}
|
|
129
|
+
if (confirmResult === 'absorbed') return true;
|
|
129
130
|
|
|
130
131
|
// Search mode
|
|
131
132
|
if (this.searching) {
|
|
@@ -200,7 +201,7 @@ export class SessionBrowserPanel extends BasePanel {
|
|
|
200
201
|
{
|
|
201
202
|
title: 'Confirmation',
|
|
202
203
|
lines: [
|
|
203
|
-
buildPanelLine(width, [[` Delete "${this.confirm.
|
|
204
|
+
buildPanelLine(width, [[` Delete "${this.confirm.subject}"?`, DEFAULT_PANEL_PALETTE.warn]]),
|
|
204
205
|
buildPanelLine(width, [[' y', DEFAULT_PANEL_PALETTE.info], [' confirm delete', DEFAULT_PANEL_PALETTE.dim], [' n / Esc', DEFAULT_PANEL_PALETTE.info], [' cancel', DEFAULT_PANEL_PALETTE.dim]]),
|
|
205
206
|
],
|
|
206
207
|
},
|
|
@@ -378,13 +379,13 @@ export class SessionBrowserPanel extends BasePanel {
|
|
|
378
379
|
private _promptDelete(): void {
|
|
379
380
|
const sess = this.filtered[this.cursorIndex];
|
|
380
381
|
if (!sess) return;
|
|
381
|
-
this.confirm = {
|
|
382
|
+
this.confirm = { subject: sess.name, label: sess.name };
|
|
382
383
|
this.markDirty();
|
|
383
384
|
}
|
|
384
385
|
|
|
385
386
|
private _deleteConfirmed(): void {
|
|
386
387
|
if (!this.confirm) return;
|
|
387
|
-
const name = this.confirm.
|
|
388
|
+
const name = this.confirm.subject;
|
|
388
389
|
this.confirm = null;
|
|
389
390
|
try {
|
|
390
391
|
this.sessionManager.delete(name);
|
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
import type { Line } from '../types/grid.ts';
|
|
2
|
-
import {
|
|
3
|
-
import { BasePanel } from './base-panel.ts';
|
|
2
|
+
import { ScrollableListPanel } from './scrollable-list-panel.ts';
|
|
4
3
|
import {
|
|
5
4
|
buildDetailBlock,
|
|
6
5
|
buildGuidanceLine,
|
|
7
6
|
buildPanelListRow,
|
|
8
7
|
buildPanelLine,
|
|
9
|
-
|
|
8
|
+
buildStatusPill,
|
|
10
9
|
buildSummaryBlock,
|
|
11
10
|
DEFAULT_PANEL_PALETTE,
|
|
12
|
-
|
|
13
|
-
type PanelWorkspaceSection,
|
|
11
|
+
type PanelPalette,
|
|
14
12
|
} from './polish.ts';
|
|
15
13
|
import { getSettingsControlPlaneSnapshot } from '@pellux/goodvibes-sdk/platform/runtime/settings/control-plane';
|
|
16
14
|
import type { ConfigManager } from '../config/index.ts';
|
|
@@ -24,141 +22,99 @@ const C = {
|
|
|
24
22
|
error: '#ef4444',
|
|
25
23
|
} as const;
|
|
26
24
|
|
|
27
|
-
|
|
28
|
-
private selectedIndex = 0;
|
|
29
|
-
private scrollOffset = 0;
|
|
25
|
+
type ResolvedEntry = ReturnType<typeof getSettingsControlPlaneSnapshot>['resolvedEntries'][number];
|
|
30
26
|
|
|
27
|
+
export class SettingsSyncPanel extends ScrollableListPanel<ResolvedEntry> {
|
|
31
28
|
public constructor(private readonly configManager: ConfigManager) {
|
|
32
29
|
super('settings-sync', 'Settings Sync', 'S', 'monitoring');
|
|
30
|
+
this.showSelectionGutter = true; // I5: non-color selection affordance
|
|
33
31
|
}
|
|
34
32
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
33
|
+
protected override getPalette(): PanelPalette {
|
|
34
|
+
return C;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
protected getItems(): readonly ResolvedEntry[] {
|
|
38
|
+
return getSettingsControlPlaneSnapshot(this.configManager).resolvedEntries;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
protected renderItem(entry: ResolvedEntry, _index: number, selected: boolean, width: number): Line {
|
|
42
|
+
return buildPanelListRow(width, [
|
|
43
|
+
{ text: entry.key.padEnd(32), fg: C.value },
|
|
44
|
+
{ text: ` ${entry.effectiveSource}`.padEnd(11), fg: entry.effectiveSource === 'managed' ? C.warn : entry.effectiveSource === 'synced' ? C.ok : entry.effectiveSource === 'local' ? C.info : C.dim },
|
|
45
|
+
{ text: `${String(entry.effectiveValue)}`.slice(0, Math.max(0, width - 47)), fg: entry.locked ? C.warn : C.dim },
|
|
46
|
+
], C, { selected });
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
protected override getEmptyStateMessage(): string {
|
|
50
|
+
return ' No resolved settings entries.';
|
|
49
51
|
}
|
|
50
52
|
|
|
51
53
|
public render(width: number, height: number): Line[] {
|
|
52
|
-
this.needsRender = false;
|
|
53
54
|
const snapshot = getSettingsControlPlaneSnapshot(this.configManager);
|
|
54
|
-
|
|
55
|
-
this.selectedIndex = safeSelectedIndex;
|
|
55
|
+
|
|
56
56
|
const postureLines: Line[] = [
|
|
57
|
-
buildPanelLine(width, [[' resolved keys ', C.label], [String(snapshot.resolvedEntries.length), C.value], [' conflicts ', C.label],
|
|
57
|
+
buildPanelLine(width, [[' resolved keys ', C.label], [String(snapshot.resolvedEntries.length), C.value], [' conflicts ', C.label], ...buildStatusPill(snapshot.conflicts.length > 0 ? 'bad' : 'good', String(snapshot.conflicts.length)), [' failures ', C.label], ...buildStatusPill(snapshot.recentFailures.length > 0 ? 'warn' : 'good', String(snapshot.recentFailures.length))]),
|
|
58
58
|
buildPanelLine(width, [[' managed locks ', C.label], [String(snapshot.managedLockCount), snapshot.managedLockCount > 0 ? C.warn : C.dim], [' staged bundle ', C.label], [snapshot.stagedManagedBundle ? snapshot.stagedManagedBundle.profileName : 'none', snapshot.stagedManagedBundle ? C.info : C.dim]]),
|
|
59
59
|
buildGuidanceLine(width, '/settingssync conflicts', 'review conflicting synced values before they silently shape effective configuration', C),
|
|
60
60
|
buildGuidanceLine(width, '/managed review', 'inspect staged managed changes, risk posture, and rollback records', C),
|
|
61
61
|
];
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
{
|
|
96
|
-
title: 'Failures',
|
|
97
|
-
lines: snapshot.recentFailures.length > 0
|
|
98
|
-
? snapshot.recentFailures.map((failure) => buildPanelLine(width, [[` ${failure.surface}`.padEnd(10), C.error], [` ${failure.message}`.slice(0, Math.max(0, width - 12)), C.dim]]))
|
|
99
|
-
: [buildPanelLine(width, [[' No recent sync or managed-setting failures.', C.dim]])],
|
|
100
|
-
},
|
|
101
|
-
{
|
|
102
|
-
title: 'Conflicts',
|
|
103
|
-
lines: snapshot.conflicts.length > 0
|
|
104
|
-
? snapshot.conflicts.map((conflict) => buildPanelLine(width, [[` ${conflict.key}`.padEnd(30), C.value], [` ${conflict.source}`.padEnd(10), C.warn], [` resolve: /settingssync resolve ${conflict.key} local|synced`.slice(0, Math.max(0, width - 42)), C.dim]]))
|
|
105
|
-
: [buildPanelLine(width, [[' No settings conflicts detected.', C.dim]])],
|
|
106
|
-
},
|
|
107
|
-
{
|
|
108
|
-
title: 'Rollback History',
|
|
109
|
-
lines: snapshot.rollbackHistory.length > 0
|
|
110
|
-
? snapshot.rollbackHistory.map((entry) => buildPanelLine(width, [[` ${entry.token}`.padEnd(18), C.info], [` ${entry.profileName}`.padEnd(18), C.value], [` restored=${String(entry.restoredKeys.length).padEnd(4)}`, C.warn], [` ${new Date(entry.appliedAt).toLocaleString()}`.slice(0, Math.max(0, width - 46)), C.dim]]))
|
|
111
|
-
: [buildPanelLine(width, [[' No managed rollback records yet.', C.dim]])],
|
|
112
|
-
},
|
|
62
|
+
|
|
63
|
+
const headerLines: Line[] = [
|
|
64
|
+
...buildSummaryBlock(width, 'Settings posture', postureLines, C),
|
|
65
|
+
buildPanelLine(width, [[' local typed config ', C.label], [String(snapshot.liveKeyCount), C.value], [' saved profiles ', C.label], [String(snapshot.profileCount), C.info], [' managed locks ', C.label], [String(snapshot.managedLockCount), snapshot.managedLockCount > 0 ? C.warn : C.dim]]),
|
|
66
|
+
buildPanelLine(width, [[' effective local ', C.label], [String(snapshot.resolvedCounts.local), C.info], [' synced ', C.label], [String(snapshot.resolvedCounts.synced), snapshot.resolvedCounts.synced > 0 ? C.ok : C.dim], [' managed ', C.label], [String(snapshot.resolvedCounts.managed), snapshot.resolvedCounts.managed > 0 ? C.warn : C.dim]]),
|
|
67
|
+
buildPanelLine(width, [[' last sync ', C.label], [snapshot.lastSync ? `${snapshot.lastSync.surface}/${snapshot.lastSync.direction}` : 'none', snapshot.lastSync ? C.ok : C.dim], [' when ', C.label], [snapshot.lastSync ? new Date(snapshot.lastSync.timestamp).toLocaleString() : 'n/a', C.dim]]),
|
|
68
|
+
// Staged Bundle
|
|
69
|
+
...(snapshot.stagedManagedBundle
|
|
70
|
+
? [
|
|
71
|
+
buildPanelLine(width, [[' profile ', C.label], [snapshot.stagedManagedBundle.profileName, C.value], [' risk ', C.label], [snapshot.stagedManagedBundle.risk, snapshot.stagedManagedBundle.risk === 'high' ? C.error : snapshot.stagedManagedBundle.risk === 'medium' ? C.warn : C.ok], [' changes ', C.label], [String(snapshot.stagedManagedBundle.changeCount), C.info]]),
|
|
72
|
+
buildPanelLine(width, [[' path ', C.label], [snapshot.stagedManagedBundle.path.slice(0, Math.max(0, width - 9)), C.dim]]),
|
|
73
|
+
]
|
|
74
|
+
: [buildPanelLine(width, [[' No staged managed settings bundle.', C.dim]])]),
|
|
75
|
+
// Recent Events
|
|
76
|
+
...(snapshot.recentEvents.length > 0
|
|
77
|
+
? snapshot.recentEvents.map((event) => buildPanelLine(width, [[` ${event.surface}/${event.direction}`.padEnd(18), C.info], [` ${event.detail}`.slice(0, Math.max(0, width - 20)), C.dim]]))
|
|
78
|
+
: [buildPanelLine(width, [[' No sync or managed-setting events recorded yet.', C.dim]])]),
|
|
79
|
+
// Managed Locks
|
|
80
|
+
...(snapshot.managedLocks.length > 0
|
|
81
|
+
? snapshot.managedLocks.slice(0, 10).map((lock) => buildPanelLine(width, [[` ${lock.key}`.padEnd(30), C.value], [` source=${lock.source}`.padEnd(24), C.info], [` ${lock.reason}`.slice(0, Math.max(0, width - 56)), C.dim]]))
|
|
82
|
+
: [buildPanelLine(width, [[' No managed locks are currently active.', C.dim]])]),
|
|
83
|
+
// Failures
|
|
84
|
+
...(snapshot.recentFailures.length > 0
|
|
85
|
+
? snapshot.recentFailures.map((failure) => buildPanelLine(width, [[` ${failure.surface}`.padEnd(10), C.error], [` ${failure.message}`.slice(0, Math.max(0, width - 12)), C.dim]]))
|
|
86
|
+
: [buildPanelLine(width, [[' No recent sync or managed-setting failures.', C.dim]])]),
|
|
87
|
+
// Conflicts
|
|
88
|
+
...(snapshot.conflicts.length > 0
|
|
89
|
+
? snapshot.conflicts.map((conflict) => buildPanelLine(width, [[` ${conflict.key}`.padEnd(30), C.value], [` ${conflict.source}`.padEnd(10), C.warn], [` resolve: /settingssync resolve ${conflict.key} local|synced`.slice(0, Math.max(0, width - 42)), C.dim]]))
|
|
90
|
+
: [buildPanelLine(width, [[' No settings conflicts detected.', C.dim]])]),
|
|
91
|
+
// Rollback History
|
|
92
|
+
...(snapshot.rollbackHistory.length > 0
|
|
93
|
+
? snapshot.rollbackHistory.map((entry) => buildPanelLine(width, [[` ${entry.token}`.padEnd(18), C.info], [` ${entry.profileName}`.padEnd(18), C.value], [` restored=${String(entry.restoredKeys.length).padEnd(4)}`, C.warn], [` ${new Date(entry.appliedAt).toLocaleString()}`.slice(0, Math.max(0, width - 46)), C.dim]]))
|
|
94
|
+
: [buildPanelLine(width, [[' No managed rollback records yet.', C.dim]])]),
|
|
113
95
|
];
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
beforeSections: prefixSections,
|
|
131
|
-
section: {
|
|
132
|
-
title: 'Resolved Entries',
|
|
133
|
-
scrollableLines: snapshot.resolvedEntries.map((entry, absolute) => {
|
|
134
|
-
return buildPanelListRow(width, [
|
|
135
|
-
{ text: entry.key.padEnd(32), fg: C.value },
|
|
136
|
-
{ text: ` ${entry.effectiveSource}`.padEnd(11), fg: entry.effectiveSource === 'managed' ? C.warn : entry.effectiveSource === 'synced' ? C.ok : entry.effectiveSource === 'local' ? C.info : C.dim },
|
|
137
|
-
{ text: `${String(entry.effectiveValue)}`.slice(0, Math.max(0, width - 47)), fg: entry.locked ? C.warn : C.dim },
|
|
138
|
-
], C, { selected: absolute === this.selectedIndex });
|
|
139
|
-
}),
|
|
140
|
-
selectedIndex: this.selectedIndex,
|
|
141
|
-
scrollOffset: this.scrollOffset,
|
|
142
|
-
guardRows: 1,
|
|
143
|
-
minRows: 4,
|
|
144
|
-
appendWindowSummary: { dimColor: C.dim },
|
|
145
|
-
},
|
|
146
|
-
afterSections: selectedSections,
|
|
147
|
-
});
|
|
148
|
-
this.scrollOffset = resolvedEntriesSection.scrollOffset;
|
|
149
|
-
const sections: PanelWorkspaceSection[] = [
|
|
150
|
-
...prefixSections,
|
|
151
|
-
resolvedEntriesSection.section,
|
|
152
|
-
...selectedSections,
|
|
96
|
+
|
|
97
|
+
this.clampSelection();
|
|
98
|
+
const selectedEntry = snapshot.resolvedEntries[this.selectedIndex];
|
|
99
|
+
const footerLines: Line[] = [
|
|
100
|
+
...(selectedEntry
|
|
101
|
+
? buildDetailBlock(width, 'Selected setting', [
|
|
102
|
+
buildPanelLine(width, [[' key ', C.label], [selectedEntry.key, C.value], [' category ', C.label], [selectedEntry.category, C.info]]),
|
|
103
|
+
buildPanelLine(width, [[' effective ', C.label], [selectedEntry.effectiveSource, selectedEntry.effectiveSource === 'managed' ? C.warn : selectedEntry.effectiveSource === 'synced' ? C.ok : selectedEntry.effectiveSource === 'local' ? C.info : C.dim], [' locked ', C.label], [selectedEntry.locked ? 'yes' : 'no', selectedEntry.locked ? C.warn : C.dim], [' conflict ', C.label], [selectedEntry.conflict ? 'yes' : 'no', selectedEntry.conflict ? C.error : C.good]]),
|
|
104
|
+
buildPanelLine(width, [[' source ', C.label], [(selectedEntry.sourceLabel ?? 'local/default').slice(0, Math.max(0, width - 10)), C.dim]]),
|
|
105
|
+
buildPanelLine(width, [[' overrides ', C.label], [(selectedEntry.overriddenSources.length > 0 ? selectedEntry.overriddenSources.join(', ') : 'none').slice(0, Math.max(0, width - 13)), C.dim]]),
|
|
106
|
+
buildPanelLine(width, [[' local ', C.label], [String(selectedEntry.localValue).slice(0, Math.max(0, width - 9)), C.dim]]),
|
|
107
|
+
buildPanelLine(width, [[' synced ', C.label], [String(selectedEntry.syncedValue ?? '(unset)').slice(0, Math.max(0, width - 10)), C.ok]]),
|
|
108
|
+
buildPanelLine(width, [[' managed ', C.label], [String(selectedEntry.managedValue ?? '(unset)').slice(0, Math.max(0, width - 11)), C.warn]]),
|
|
109
|
+
], C)
|
|
110
|
+
: []),
|
|
111
|
+
buildPanelLine(width, [[' ↑/↓ browse /settingssync show <key> /settingssync resolve <key> <local|synced> /managed apply-staged [key...] ', C.dim]]),
|
|
153
112
|
];
|
|
154
|
-
|
|
113
|
+
|
|
114
|
+
return this.renderList(width, height, {
|
|
155
115
|
title: 'Settings Sync',
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
footerLines: [buildPanelLine(width, [[' ↑/↓ browse /settingssync show <key> /settingssync resolve <key> <local|synced> /managed apply-staged [key...] ', C.dim]])],
|
|
159
|
-
palette: C,
|
|
116
|
+
header: headerLines,
|
|
117
|
+
footer: footerLines,
|
|
160
118
|
});
|
|
161
|
-
while (lines.length < height) lines.push(createEmptyLine(width));
|
|
162
|
-
return lines.slice(0, height);
|
|
163
119
|
}
|
|
164
120
|
}
|