@pellux/goodvibes-tui 1.16.1 → 1.17.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 +89 -9
- package/README.md +1 -1
- package/docs/foundation-artifacts/operator-contract.json +1091 -271
- package/package.json +7 -7
- package/src/cli/completions/generate.ts +5 -4
- package/src/cli/config-overrides.ts +51 -11
- package/src/cli/doctor.ts +4 -5
- package/src/cli/entrypoint.ts +13 -8
- package/src/cli/help.ts +7 -6
- package/src/cli/launch-auto-update.ts +214 -0
- package/src/cli/parser.ts +5 -0
- package/src/cli/status.ts +13 -0
- package/src/cli/surface-command.ts +5 -5
- package/src/cli/tui-startup.ts +60 -97
- package/src/cli/types.ts +2 -0
- package/src/config/index.ts +1 -1
- package/src/config/tui-extension-settings.ts +33 -0
- package/src/core/composer-state.ts +1 -1
- package/src/core/conversation-line-cache.ts +3 -3
- package/src/core/conversation-rendering.ts +4 -4
- package/src/core/conversation.ts +2 -2
- package/src/core/long-task-notifier.ts +7 -7
- package/src/core/stream-event-wiring.ts +8 -6
- package/src/core/system-message-noise.ts +1 -1
- package/src/core/system-message-router.ts +6 -6
- package/src/core/turn-cancellation.ts +1 -1
- package/src/core/turn-event-wiring.ts +8 -8
- package/src/daemon/cli.ts +20 -5
- package/src/daemon/lifecycle.ts +41 -0
- package/src/daemon/service-commands.ts +5 -5
- package/src/export/cost-utils.ts +128 -6
- package/src/input/autocomplete.ts +2 -2
- package/src/input/command-registry.ts +34 -9
- package/src/input/commands/ci-runtime.ts +5 -1
- package/src/input/commands/codebase-runtime.ts +6 -6
- package/src/input/commands/cost-runtime.ts +1 -1
- package/src/input/commands/health-runtime.ts +2 -2
- package/src/input/commands/intelligence-runtime.ts +1 -1
- package/src/input/commands/local-auth-runtime.ts +1 -1
- package/src/input/commands/local-provider-runtime.ts +49 -10
- package/src/input/commands/local-runtime.ts +53 -5
- package/src/input/commands/memory.ts +2 -2
- package/src/input/commands/operator-panel-runtime.ts +5 -5
- package/src/input/commands/permissions-provenance.ts +1 -1
- package/src/input/commands/permissions-runtime.ts +99 -8
- package/src/input/commands/planning-runtime.ts +3 -3
- package/src/input/commands/platform-sandbox-runtime.ts +1 -1
- package/src/input/commands/provider-accounts-runtime.ts +71 -6
- package/src/input/commands/provider.ts +7 -5
- package/src/input/commands/recall-files-git.ts +13 -51
- package/src/input/commands/recall-files.ts +8 -11
- package/src/input/commands/recall-review.ts +2 -2
- package/src/input/commands/relay-runtime.ts +1 -1
- package/src/input/commands/remote-runtime.ts +1 -1
- package/src/input/commands/runtime-services.ts +4 -4
- package/src/input/commands/schedule-runtime.ts +14 -7
- package/src/input/commands/services-runtime.ts +1 -1
- package/src/input/commands/session-workflow.ts +2 -2
- package/src/input/commands/settings-sync-runtime.ts +2 -2
- package/src/input/commands/share-runtime.ts +1 -1
- package/src/input/commands/shell-core.ts +46 -68
- package/src/input/commands/subscription-runtime.ts +1 -1
- package/src/input/commands/update-runtime.ts +153 -96
- package/src/input/commands/work-plan-runtime.ts +1 -1
- package/src/input/commands/workstream-runtime.ts +2 -2
- package/src/input/commands.ts +0 -2
- package/src/input/config-modal-types.ts +3 -3
- package/src/input/config-modal.ts +10 -11
- package/src/input/feature-unit-layout.ts +93 -82
- package/src/input/feed-context-factory.ts +2 -2
- package/src/input/handler-command-route.ts +2 -2
- package/src/input/handler-content-actions.ts +1 -1
- package/src/input/handler-feed-routes.ts +7 -7
- package/src/input/handler-feed.ts +4 -4
- package/src/input/handler-interactions.ts +3 -2
- package/src/input/handler-modal-routes.ts +12 -4
- package/src/input/handler-modal-token-routes.ts +1 -1
- package/src/input/handler-onboarding-daemon-adopt.ts +2 -2
- package/src/input/handler-onboarding.ts +3 -2
- package/src/input/handler-picker-routes.ts +26 -13
- package/src/input/handler-shortcuts.ts +6 -6
- package/src/input/handler.ts +2 -2
- package/src/input/keybindings.ts +2 -2
- package/src/input/onboarding/onboarding-feature-units.ts +34 -27
- package/src/input/onboarding/onboarding-provider-key-fields.ts +112 -0
- package/src/input/onboarding/onboarding-wizard-apply.ts +22 -21
- package/src/input/onboarding/onboarding-wizard-external-surfaces.ts +17 -0
- package/src/input/onboarding/onboarding-wizard-network-adopt.ts +2 -2
- package/src/input/onboarding/onboarding-wizard-steps.ts +30 -42
- package/src/input/panel-integration-actions.ts +2 -2
- package/src/input/panel-mouse-geometry.ts +1 -1
- package/src/input/panel-paste-flood-guard.ts +1 -1
- package/src/input/provider-key-intake.ts +167 -0
- package/src/input/sandbox-exec-config.ts +2 -2
- package/src/input/selection-modal.ts +1 -1
- package/src/input/settings-modal-activation.ts +5 -0
- package/src/input/settings-modal-adjustment.ts +4 -31
- package/src/input/settings-modal-data.ts +22 -21
- package/src/input/settings-modal-mutations.ts +12 -68
- package/src/input/settings-modal-types.ts +23 -16
- package/src/input/settings-modal.ts +74 -55
- package/src/main.ts +48 -48
- package/src/panels/agent-inspector-shared.ts +4 -4
- package/src/panels/base-panel.ts +2 -2
- package/src/panels/builtin/agent.ts +4 -4
- package/src/panels/builtin/development.ts +4 -4
- package/src/panels/builtin/knowledge.ts +1 -1
- package/src/panels/builtin/operations.ts +29 -10
- package/src/panels/builtin/session.ts +5 -5
- package/src/panels/builtin/shared.ts +4 -4
- package/src/panels/builtin-modals.ts +10 -10
- package/src/panels/builtin-panels.ts +1 -1
- package/src/panels/cost-tracker-panel.ts +124 -7
- package/src/panels/diff-panel.ts +5 -5
- package/src/panels/eval-registry.ts +2 -2
- package/src/panels/fleet-deep-link.ts +1 -1
- package/src/panels/fleet-panel-format.ts +44 -8
- package/src/panels/fleet-panel.ts +16 -16
- package/src/panels/fleet-read-model.ts +52 -13
- package/src/panels/fleet-steer.ts +2 -2
- package/src/panels/fleet-stop.ts +3 -3
- package/src/panels/fleet-tabs.ts +13 -14
- package/src/panels/fleet-transcript.ts +5 -5
- package/src/panels/git-panel.ts +2 -2
- package/src/panels/index.ts +4 -1
- package/src/panels/local-auth-panel.ts +1 -1
- package/src/panels/modals/hooks-modal.ts +1 -1
- package/src/panels/modals/keybindings-modal.ts +2 -2
- package/src/panels/modals/knowledge-modal.ts +1 -1
- package/src/panels/modals/marketplace-modal.ts +4 -4
- package/src/panels/modals/memory-modal.ts +4 -4
- package/src/panels/modals/modal-surface-helpers.ts +1 -1
- package/src/panels/modals/modal-theme.ts +1 -1
- package/src/panels/modals/pairing-modal.ts +2 -2
- package/src/panels/modals/planning-modal.ts +2 -2
- package/src/panels/modals/plugins-modal.ts +1 -1
- package/src/panels/modals/policy-modal.ts +1 -1
- package/src/panels/modals/security-modal.ts +1 -1
- package/src/panels/modals/skills-modal.ts +1 -1
- package/src/panels/modals/work-plan-modal.ts +3 -3
- package/src/panels/notifications-feed.ts +127 -0
- package/src/panels/notifications-panel.ts +160 -0
- package/src/panels/panel-manager.ts +9 -9
- package/src/panels/plugins-panel.ts +4 -4
- package/src/panels/project-planning-answer-actions.ts +1 -1
- package/src/panels/provider-health-routes.ts +2 -2
- package/src/panels/skills-panel.ts +4 -5
- package/src/panels/token-budget-panel.ts +11 -3
- package/src/panels/types.ts +5 -5
- package/src/permissions/broker-approval-card.ts +201 -0
- package/src/permissions/hunk-selection.ts +28 -4
- package/src/permissions/prompt.ts +244 -29
- package/src/renderer/autocomplete-overlay.ts +107 -54
- package/src/renderer/code-block.ts +2 -2
- package/src/renderer/compaction-history-modal.ts +1 -1
- package/src/renderer/compaction-preview.ts +1 -1
- package/src/renderer/compaction-quality.ts +1 -1
- package/src/renderer/compositor.ts +2 -2
- package/src/renderer/config-modal.ts +1 -1
- package/src/renderer/conversation-overlays.ts +1 -1
- package/src/renderer/footer-tips.ts +3 -3
- package/src/renderer/hint-grammar.ts +1 -1
- package/src/renderer/modal-factory.ts +1 -1
- package/src/renderer/model-picker-overlay.ts +1 -1
- package/src/renderer/model-workspace.ts +7 -2
- package/src/renderer/panel-composite.ts +2 -2
- package/src/renderer/panel-workspace-bar.ts +1 -1
- package/src/renderer/process-indicator.ts +1 -1
- package/src/renderer/selection-modal-overlay.ts +1 -1
- package/src/renderer/session-picker-modal.ts +2 -2
- package/src/renderer/settings-modal-helpers.ts +14 -5
- package/src/renderer/settings-modal.ts +127 -39
- package/src/renderer/shell-surface.ts +3 -0
- package/src/renderer/terminal-bg-probe.ts +1 -1
- package/src/renderer/theme.ts +3 -3
- package/src/renderer/tool-call.ts +1 -1
- package/src/renderer/tool-result-summary.ts +1 -1
- package/src/renderer/turn-injection.ts +7 -6
- package/src/renderer/ui-factory.ts +6 -1
- package/src/runtime/bootstrap-command-context.ts +4 -1
- package/src/runtime/bootstrap-command-parts.ts +6 -4
- package/src/runtime/bootstrap-core.ts +35 -35
- package/src/runtime/bootstrap-hook-bridge.ts +1 -1
- package/src/runtime/bootstrap-shell.ts +6 -5
- package/src/runtime/bootstrap.ts +34 -32
- package/src/runtime/channel-composition.ts +46 -0
- package/src/runtime/code-index-services.ts +3 -4
- package/src/runtime/daemon-attach-notices.ts +120 -0
- package/src/runtime/durability-services.ts +55 -0
- package/src/runtime/embedded-service-factories.ts +40 -0
- package/src/runtime/feature-settings.ts +147 -0
- package/src/runtime/index.ts +4 -21
- package/src/runtime/legacy-daemon-migration.ts +29 -19
- package/src/runtime/memory-fold.ts +1 -1
- package/src/runtime/notification-dispatch.ts +110 -0
- package/src/runtime/onboarding/apply.ts +3 -29
- package/src/runtime/onboarding/fast-path.ts +39 -6
- package/src/runtime/onboarding/provider-key-capture.ts +126 -0
- package/src/runtime/onboarding/snapshot.ts +0 -1
- package/src/runtime/onboarding/types.ts +9 -3
- package/src/runtime/provider-fallback.ts +55 -0
- package/src/runtime/provider-optimizer-wiring.ts +55 -0
- package/src/runtime/release-artifacts.ts +19 -103
- package/src/runtime/resume-notice.ts +1 -1
- package/src/runtime/services.ts +81 -82
- package/src/runtime/session-continuity-hints.ts +38 -0
- package/src/runtime/surface-feature-flags.ts +40 -9
- package/src/runtime/terminal-output-guard.ts +2 -2
- package/src/runtime/trust/workspace-registration.ts +10 -2
- package/src/runtime/trust/workspace-trust.ts +58 -31
- package/src/runtime/ui-services.ts +3 -1
- package/src/runtime/update-check.ts +15 -69
- package/src/runtime/workstream-services.ts +2 -2
- package/src/shell/blocking-input.ts +102 -6
- package/src/shell/ui-openers.ts +38 -6
- package/src/utils/format-duration.ts +1 -1
- package/src/verification/verification-ledger.ts +21 -4
- package/src/version.ts +1 -1
- package/src/input/commands/flags-runtime.ts +0 -328
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type Line } from '../types/grid.ts';
|
|
2
2
|
import { fitDisplay, getDisplayWidth, truncateDisplay } from '../utils/terminal-width.ts';
|
|
3
|
+
import { wrapWithHangingIndent } from './text-layout.ts';
|
|
3
4
|
import type { AutocompleteEngine } from '../input/autocomplete.ts';
|
|
4
5
|
import {
|
|
5
6
|
createOverlayBorderLine,
|
|
@@ -29,6 +30,14 @@ function putText(line: Line, startX: number, maxWidth: number, text: string, sty
|
|
|
29
30
|
|
|
30
31
|
/**
|
|
31
32
|
* Render the slash command autocomplete dropdown as Line[] for overlay in the viewport.
|
|
33
|
+
*
|
|
34
|
+
* A row's description renders inline only when it genuinely fits beside the
|
|
35
|
+
* command name; otherwise the FULL description wraps onto its own line(s)
|
|
36
|
+
* below the command name, with no line cap — descriptive text is never
|
|
37
|
+
* clipped (same fix as selection-modal-overlay.ts's modal rule). Item
|
|
38
|
+
* visibility is row-budget aware (a wrapped description can cost more than
|
|
39
|
+
* one physical row), always showing the selected item in full and falling
|
|
40
|
+
* back to the existing position counter rather than losing text.
|
|
32
41
|
*/
|
|
33
42
|
export function renderAutocompleteOverlay(
|
|
34
43
|
autocomplete: AutocompleteEngine,
|
|
@@ -39,12 +48,15 @@ export function renderAutocompleteOverlay(
|
|
|
39
48
|
if (!state.active || state.results.length === 0) return [];
|
|
40
49
|
|
|
41
50
|
const lines: Line[] = [];
|
|
51
|
+
const chromeRows = 4;
|
|
42
52
|
const metrics = getOverlaySurfaceMetrics(width, viewportHeight, {
|
|
43
53
|
margin: 2,
|
|
44
54
|
maxWidth: 88,
|
|
45
|
-
chromeRows
|
|
55
|
+
chromeRows,
|
|
46
56
|
minContentRows: 6,
|
|
47
|
-
|
|
57
|
+
// Rows grow with wrapped descriptions (never clipped) rather than
|
|
58
|
+
// staying pinned at a small constant — still bounded by the viewport.
|
|
59
|
+
maxContentRows: Math.max(10, viewportHeight - chromeRows - 4),
|
|
48
60
|
});
|
|
49
61
|
const layout = createOverlayBoxLayout(width, metrics.margin, metrics.boxWidth);
|
|
50
62
|
|
|
@@ -68,88 +80,129 @@ export function renderAutocompleteOverlay(
|
|
|
68
80
|
|
|
69
81
|
const results = state.results;
|
|
70
82
|
const total = results.length;
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
total - maxVisible,
|
|
79
|
-
),
|
|
80
|
-
);
|
|
81
|
-
}
|
|
82
|
-
const endIdx = Math.min(startIdx + maxVisible, total);
|
|
83
|
+
const rowBudget = Math.max(1, metrics.contentRows);
|
|
84
|
+
|
|
85
|
+
const indicatorWidth = 2;
|
|
86
|
+
const maxCommandWidth = Math.min(18, Math.max(10, Math.floor(layout.innerWidth * 0.28)));
|
|
87
|
+
const gapWidth = 2;
|
|
88
|
+
const descWidth = Math.max(0, layout.innerWidth - indicatorWidth - maxCommandWidth - gapWidth);
|
|
89
|
+
const wrapWidth = Math.max(8, layout.innerWidth - indicatorWidth);
|
|
83
90
|
|
|
84
|
-
//
|
|
91
|
+
// palette curation (item 4): on a bare '/' (query === ''), the results
|
|
85
92
|
// list is "common tier" (score 2) followed by "alphabetical rest" (score
|
|
86
93
|
// 1) — see CommandRegistry.fuzzyMatch. commonCount marks that boundary;
|
|
87
|
-
//
|
|
88
|
-
// consuming one of the maxVisible slots so the box height never changes.
|
|
94
|
+
// a one-row separator draws there when it falls inside the visible window.
|
|
89
95
|
const hasCommonSeparator = state.query === '' && state.commonCount > 0 && state.commonCount < total;
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
if (hasCommonSeparator && i === state.commonCount) {
|
|
94
|
-
displayRows.push({ type: 'separator' });
|
|
95
|
-
if (displayRows.length >= maxVisible) break;
|
|
96
|
-
}
|
|
97
|
-
displayRows.push({ type: 'item', index: i });
|
|
96
|
+
|
|
97
|
+
function descriptionFitsInline(description: string): boolean {
|
|
98
|
+
return descWidth >= 12 && getDisplayWidth(description) <= descWidth;
|
|
98
99
|
}
|
|
99
100
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
101
|
+
/** Physical rows a result occupies: 1 for the command-name row, plus one more per wrapped description line when it doesn't fit inline, plus 1 more when the common/rest separator sits immediately before it. */
|
|
102
|
+
function rowCostFor(index: number): number {
|
|
103
|
+
const { command } = results[index];
|
|
104
|
+
const rows = descriptionFitsInline(command.description)
|
|
105
|
+
? 1
|
|
106
|
+
: 1 + wrapWithHangingIndent(command.description, wrapWidth, '').length;
|
|
107
|
+
const separatorCost = hasCommonSeparator && index === state.commonCount ? 1 : 0;
|
|
108
|
+
return rows + separatorCost;
|
|
109
|
+
}
|
|
104
110
|
|
|
105
|
-
|
|
106
|
-
|
|
111
|
+
// Row-budget-aware windowing (mirrors selection-modal-overlay.ts's fix): a
|
|
112
|
+
// window of results is chosen by their REAL rendered height rather than
|
|
113
|
+
// assuming one row per item, always including the selected item in full,
|
|
114
|
+
// growing outward (roughly centered) until the budget is spent.
|
|
115
|
+
const selectedIdx = Math.max(0, Math.min(state.selectedIndex, total - 1));
|
|
116
|
+
let startIdx = selectedIdx;
|
|
117
|
+
let endIdx = selectedIdx + 1;
|
|
118
|
+
let usedRows = rowCostFor(selectedIdx);
|
|
119
|
+
let growBefore = true;
|
|
120
|
+
while (startIdx > 0 || endIdx < total) {
|
|
121
|
+
const canGrowBefore = growBefore && startIdx > 0;
|
|
122
|
+
const canGrowAfter = !growBefore && endIdx < total;
|
|
123
|
+
if (canGrowBefore) {
|
|
124
|
+
const cost = rowCostFor(startIdx - 1);
|
|
125
|
+
if (usedRows + cost > rowBudget) break;
|
|
126
|
+
startIdx -= 1;
|
|
127
|
+
usedRows += cost;
|
|
128
|
+
} else if (canGrowAfter) {
|
|
129
|
+
const cost = rowCostFor(endIdx);
|
|
130
|
+
if (usedRows + cost > rowBudget) break;
|
|
131
|
+
endIdx += 1;
|
|
132
|
+
usedRows += cost;
|
|
133
|
+
} else if (startIdx > 0) {
|
|
134
|
+
const cost = rowCostFor(startIdx - 1);
|
|
135
|
+
if (usedRows + cost > rowBudget) break;
|
|
136
|
+
startIdx -= 1;
|
|
137
|
+
usedRows += cost;
|
|
138
|
+
} else if (endIdx < total) {
|
|
139
|
+
const cost = rowCostFor(endIdx);
|
|
140
|
+
if (usedRows + cost > rowBudget) break;
|
|
141
|
+
endIdx += 1;
|
|
142
|
+
usedRows += cost;
|
|
143
|
+
} else {
|
|
144
|
+
break;
|
|
145
|
+
}
|
|
146
|
+
growBefore = !growBefore;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
for (let i = startIdx; i < endIdx; i++) {
|
|
150
|
+
if (hasCommonSeparator && i === state.commonCount) {
|
|
107
151
|
const sepLine = createOverlayContentLine(width, layout);
|
|
108
152
|
putText(sepLine, layout.margin + 2, layout.innerWidth, '─'.repeat(layout.innerWidth), { fg: BORDER_FG, dim: true });
|
|
109
153
|
lines.push(sepLine);
|
|
110
|
-
continue;
|
|
111
154
|
}
|
|
112
|
-
|
|
155
|
+
|
|
113
156
|
const { command } = results[i];
|
|
114
157
|
const isSelected = i === state.selectedIndex;
|
|
115
|
-
const
|
|
158
|
+
const bg = isSelected ? SELECTED_BG : '';
|
|
116
159
|
const indicator = isSelected ? '▸ ' : ' ';
|
|
117
160
|
const commandText = fitDisplay(
|
|
118
161
|
truncateDisplay(`/${command.name}`, maxCommandWidth),
|
|
119
162
|
maxCommandWidth,
|
|
120
163
|
);
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
descWidth,
|
|
124
|
-
);
|
|
164
|
+
|
|
165
|
+
const line = createOverlayContentLine(width, layout, BORDER_FG, bg);
|
|
125
166
|
let x = layout.margin + 2;
|
|
126
167
|
putText(line, x, indicatorWidth, indicator, {
|
|
127
168
|
fg: isSelected ? TITLE_FG : MUTED_FG,
|
|
128
|
-
bg
|
|
169
|
+
bg,
|
|
129
170
|
bold: isSelected,
|
|
130
171
|
});
|
|
131
172
|
x += indicatorWidth;
|
|
132
173
|
putText(line, x, maxCommandWidth, commandText, {
|
|
133
174
|
fg: isSelected ? TITLE_FG : BODY_FG,
|
|
134
|
-
bg
|
|
175
|
+
bg,
|
|
135
176
|
bold: isSelected,
|
|
136
177
|
});
|
|
137
178
|
x += maxCommandWidth;
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
179
|
+
|
|
180
|
+
if (descriptionFitsInline(command.description)) {
|
|
181
|
+
putText(line, x, gapWidth, ' ', { fg: BODY_FG, bg });
|
|
182
|
+
x += gapWidth;
|
|
183
|
+
putText(line, x, descWidth, fitDisplay(command.description, descWidth), {
|
|
184
|
+
fg: isSelected ? BODY_FG : MUTED_FG,
|
|
185
|
+
bg,
|
|
186
|
+
bold: false,
|
|
187
|
+
});
|
|
188
|
+
lines.push(line);
|
|
189
|
+
} else {
|
|
190
|
+
// Doesn't fit beside the command name — wrap the FULL description onto
|
|
191
|
+
// its own line(s) below at the full row width, rather than clipping it.
|
|
192
|
+
lines.push(line);
|
|
193
|
+
for (const wrapped of wrapWithHangingIndent(command.description, wrapWidth, '')) {
|
|
194
|
+
const descLine = createOverlayContentLine(width, layout, BORDER_FG, bg);
|
|
195
|
+
putText(descLine, layout.margin + 2 + indicatorWidth, wrapWidth, fitDisplay(wrapped, wrapWidth), {
|
|
196
|
+
fg: isSelected ? BODY_FG : MUTED_FG,
|
|
197
|
+
bg,
|
|
198
|
+
});
|
|
199
|
+
lines.push(descLine);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
149
202
|
}
|
|
150
203
|
|
|
151
|
-
if (
|
|
152
|
-
|
|
204
|
+
if (startIdx > 0 || endIdx < total) {
|
|
205
|
+
const scrollLine = createOverlayContentLine(width, layout);
|
|
153
206
|
const scrollText = `${state.selectedIndex + 1}/${total}`;
|
|
154
207
|
putText(
|
|
155
208
|
scrollLine,
|
|
@@ -162,7 +215,7 @@ export function renderAutocompleteOverlay(
|
|
|
162
215
|
}
|
|
163
216
|
|
|
164
217
|
const footerLine = createOverlayContentLine(width, layout);
|
|
165
|
-
//
|
|
218
|
+
// vocab unification: "Run"/"Close" match the /help selection modal's
|
|
166
219
|
// hint bar (selection-modal-overlay.ts) — both surfaces execute a command
|
|
167
220
|
// on Enter, so they now say so the same way.
|
|
168
221
|
const hints = '[Tab] Complete [Up/Down] Navigate [Enter] Run [Esc] Close';
|
|
@@ -11,11 +11,11 @@ import { UI_TONES } from './ui-primitives.ts';
|
|
|
11
11
|
* property/comment) are pinned to the same values as the tree-sitter
|
|
12
12
|
* Vaporwave palette in syntax-highlighter.ts's TOKEN_STYLES map, so a code
|
|
13
13
|
* block that starts on the regex fallback and gets replaced by the async
|
|
14
|
-
* tree-sitter parse doesn't visibly shift color mid-stream
|
|
14
|
+
* tree-sitter parse doesn't visibly shift color mid-stream.
|
|
15
15
|
* `accent` and `bg` are chrome (the language-label header bar and the body
|
|
16
16
|
* background), not syntax tokens, and keep their original VS Code Dark+
|
|
17
17
|
* values — folded here from two separate literals (one in the header, one
|
|
18
|
-
* duplicated on the footer line) into a single named source
|
|
18
|
+
* duplicated on the footer line) into a single named source.
|
|
19
19
|
*/
|
|
20
20
|
const FALLBACK_THEME = {
|
|
21
21
|
string: '#00ff88',
|
|
@@ -17,7 +17,7 @@ import type { CompactionQualityScore } from './compaction-quality.ts';
|
|
|
17
17
|
// ─── formatCompactionEvent ────────────────────────────────────────────────────
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
|
-
* Exported for direct unit testing (
|
|
20
|
+
* Exported for direct unit testing () — the grade suffix is a pure
|
|
21
21
|
* function of the event and an optional score, independent of whichever
|
|
22
22
|
* runtime lookup (getCompactionQualityScore) supplies that score.
|
|
23
23
|
*/
|
|
@@ -47,7 +47,7 @@ export interface CompactionAfterOptions {
|
|
|
47
47
|
/** Number of session memories that survived compaction. */
|
|
48
48
|
readonly pinnedMemoryCount: number;
|
|
49
49
|
/**
|
|
50
|
-
* Out-of-band quality-score grade for this run (
|
|
50
|
+
* Out-of-band quality-score grade for this run (). Omitted or null
|
|
51
51
|
* when no score was computed (e.g. the small-window compaction path, which
|
|
52
52
|
* has no CompactionEvent to key a score by) — no line is rendered then.
|
|
53
53
|
*/
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Compaction quality-score bridging.
|
|
3
3
|
*
|
|
4
|
-
* Reality check
|
|
4
|
+
* Reality check: the SDK's `computeQualityScore()`
|
|
5
5
|
* (platform/runtime/compaction/quality-score.ts) is only ever called from a
|
|
6
6
|
* SEPARATE pipeline, `CompactionManager` (platform/runtime/compaction/manager.ts).
|
|
7
7
|
* The TUI's actual `/compact` path never instantiates `CompactionManager` — it
|
|
@@ -95,7 +95,7 @@ export class Compositor {
|
|
|
95
95
|
|
|
96
96
|
public composite(params: CompositeRequest): void {
|
|
97
97
|
const { width, height, header, viewport, footer, selection, search, panel, panelWidth } = params;
|
|
98
|
-
//
|
|
98
|
+
// Reuse back-buffer instead of allocating each frame
|
|
99
99
|
if (!this.backBuffer) {
|
|
100
100
|
this.backBuffer = new TerminalBuffer(width, height);
|
|
101
101
|
} else {
|
|
@@ -284,7 +284,7 @@ export class Compositor {
|
|
|
284
284
|
});
|
|
285
285
|
|
|
286
286
|
// 4. Diff and Render
|
|
287
|
-
//
|
|
287
|
+
// Diff against front-buffer (last-rendered), then swap front/back — no clone() needed
|
|
288
288
|
const diff = this.diffEngine.diff(this.frontBuffer, newBuffer);
|
|
289
289
|
if (diff) {
|
|
290
290
|
allowTerminalWrite(() => this.stdout.write(diff));
|
|
@@ -22,7 +22,7 @@ export function renderConfigModal(modal: ConfigModal, width: number, height: num
|
|
|
22
22
|
// rather than overflowing (posture header + separator + chrome ≈ 8 rows).
|
|
23
23
|
const visible = Math.max(3, Math.min(16, height - 8));
|
|
24
24
|
modal.setViewportRows(visible);
|
|
25
|
-
//
|
|
25
|
+
// item 2: the list section wraps a row label at (contentW - 2),
|
|
26
26
|
// where contentW = boxW - 4 (see ModalFactory._renderListSection). Compute
|
|
27
27
|
// that exact column here so getRenderModel's wrap-clamp measures against
|
|
28
28
|
// reality instead of a fixed guess.
|
|
@@ -76,7 +76,7 @@ export function applyConversationOverlays(
|
|
|
76
76
|
next.push(...renderHistorySearchOverlay(input.historySearch, conversationWidth));
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
//
|
|
79
|
+
// retirement: the process, agent-detail, and live-tail modal overlays
|
|
80
80
|
// were removed with those modals — F2 now opens the Fleet panel, which
|
|
81
81
|
// subsumes the live process tree.
|
|
82
82
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* footer-tips.ts — the persistent discoverability hint shown in the shell
|
|
3
3
|
* footer's tip slot.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
5
|
+
* instead of a single frozen "/help for commands" line, the footer
|
|
6
6
|
* surfaces a rotating set of the highest-value affordances (panels, process
|
|
7
7
|
* monitor, help, quit). Rotation is *contextual, not timed*: when an agent turn
|
|
8
8
|
* is actively running, the process-monitor tip is promoted to the front so the
|
|
@@ -24,7 +24,7 @@ export function isAgentActive(composerStatus: string | undefined): boolean {
|
|
|
24
24
|
return composerStatus !== undefined && ACTIVE_TURN_STATUSES.has(composerStatus);
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
//
|
|
27
|
+
// Ctrl+P is a TOGGLE (open+focus when nothing is open or unfocused,
|
|
28
28
|
// hide when the workspace already has focus — see openPanelPicker in
|
|
29
29
|
// shell/ui-openers.ts). The bare noun 'panels' undersold that; naming the verb
|
|
30
30
|
// keeps the tip honest about what the chord actually does.
|
|
@@ -33,7 +33,7 @@ const TIP_PANELS = 'Ctrl+P toggle panels';
|
|
|
33
33
|
// tip names 'fleet', not 'processes'.
|
|
34
34
|
const TIP_PROCESSES = 'F2 fleet';
|
|
35
35
|
const TIP_HELP = '? help';
|
|
36
|
-
//
|
|
36
|
+
// f: an empty-composer Ctrl+C does NOT quit on the first press — it arms a
|
|
37
37
|
// ~1s "press again to exit" confirm, and only a SECOND Ctrl+C within that window
|
|
38
38
|
// exits (an intentional accidental-exit guard). The old 'Ctrl+C quit' tip
|
|
39
39
|
// implied a single press quits, so two presses seconds apart appeared to do
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* hint-grammar.ts — the single hint-bar grammar for every overlay footer.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* one grammar across the model picker, settings modal, help/shortcuts
|
|
5
5
|
* overlays, and ModalFactory variants so footers stop drifting into three
|
|
6
6
|
* different dialects (verbless brackets, prose sentences, ad-hoc separators).
|
|
7
7
|
*
|
|
@@ -195,7 +195,7 @@ export class ModalFactory {
|
|
|
195
195
|
}
|
|
196
196
|
|
|
197
197
|
// Footer / hint bar. Hint segments join with the shared middle-dot grammar
|
|
198
|
-
// separator
|
|
198
|
+
// separator so every modal footer reads as one dialect.
|
|
199
199
|
const hintStr = config.hints
|
|
200
200
|
? config.hints.join(HINT_SEPARATOR)
|
|
201
201
|
: (config.footer ?? '');
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Number of fixed chrome lines in the model-picker overlay (title + search + divider + detail×2 + footer).
|
|
3
3
|
* Used by callers to compute maxVisible item rows.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
5
|
+
* this file used to also hold `renderModelPickerOverlay`, the
|
|
6
6
|
* overlay-style model-picker renderer. It was superseded by
|
|
7
7
|
* `renderModelWorkspace` (model-workspace.ts) — conversation-overlays.ts
|
|
8
8
|
* routes `input.modelPicker.active` there, not here — and had no remaining
|
|
@@ -395,9 +395,14 @@ export function renderModelWorkspace(picker: ModelPickerModal, width: number, vi
|
|
|
395
395
|
const bodyRows = safeHeight - 3 - footerRows;
|
|
396
396
|
const maxDetailRows = Math.max(3, bodyRows - 2);
|
|
397
397
|
const minDetailRows = Math.min(6, maxDetailRows);
|
|
398
|
-
const
|
|
398
|
+
const details = detailLines(picker, contentW - 2);
|
|
399
|
+
// Never drop detail lines (the modal rule: UI-authored descriptive text is
|
|
400
|
+
// always shown in full). Size the detail band to the ACTUAL content,
|
|
401
|
+
// growing past the old fixed proportional default up to maxDetailRows —
|
|
402
|
+
// the ceiling that keeps at least a header row and one list row available —
|
|
403
|
+
// rather than silently slicing off whatever didn't fit a 32% guess.
|
|
404
|
+
const detailRows = clamp(Math.max(details.length, Math.round(bodyRows * 0.32)), minDetailRows, maxDetailRows);
|
|
399
405
|
const listRows = Math.max(1, bodyRows - detailRows - 1);
|
|
400
|
-
const details = detailLines(picker, contentW - 2).slice(0, detailRows);
|
|
401
406
|
|
|
402
407
|
for (let row = 0; row < bodyRows; row += 1) {
|
|
403
408
|
const inDetail = row < detailRows;
|
|
@@ -7,7 +7,7 @@ import { createSplitPaneLayout } from './layout-engine.ts';
|
|
|
7
7
|
import { renderPanelWorkspaceBar } from './panel-workspace-bar.ts';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
*
|
|
10
|
+
* Per-panel render cache for dirty-flag skipping.
|
|
11
11
|
*
|
|
12
12
|
* Maintainability hazard — mid-render invalidation race:
|
|
13
13
|
*
|
|
@@ -59,7 +59,7 @@ function getRenderGenState(panel: Panel): { gen: number } {
|
|
|
59
59
|
return state;
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
/**
|
|
62
|
+
/** Render a panel, skipping if nothing changed. Returns cached lines on a skip. */
|
|
63
63
|
export function renderPanel(panel: Panel, width: number, height: number): Line[] {
|
|
64
64
|
const cached = panelRenderCache.get(panel);
|
|
65
65
|
if (cached && !panel.needsRender && cached.width === width && cached.height === height) {
|
|
@@ -27,7 +27,7 @@ export function renderPanelWorkspaceBar(
|
|
|
27
27
|
// A tab can be active-but-not-focused (selected in the unfocused pane) or
|
|
28
28
|
// active-and-focused (selected in the focused pane).
|
|
29
29
|
//
|
|
30
|
-
//
|
|
30
|
+
// The pane marker uses ▲/▼ (top/bottom pane). The old '^'/'v'
|
|
31
31
|
// prefixes read as a Ctrl caret (^) implying a non-existent Ctrl+<tab>
|
|
32
32
|
// hotkey. The REAL per-tab jump key is Alt+N (panel-tab-1..9), rendered
|
|
33
33
|
// here as ⌥N for the first nine tabs so the shown affordance matches the
|
|
@@ -17,7 +17,7 @@ import { formatHints } from './hint-grammar.ts';
|
|
|
17
17
|
* Agent-count label. The footer counts only ACTIVE agents, while the fleet
|
|
18
18
|
* lists every node (running, terminal, chains, watchers). Label the count
|
|
19
19
|
* "active" so it is never misread as a grand total — the [Enter] View hint
|
|
20
|
-
* opens the fleet for the full picture. (
|
|
20
|
+
* opens the fleet for the full picture. (item 5d.)
|
|
21
21
|
*/
|
|
22
22
|
function agentCountLabel(agentCount: number): string {
|
|
23
23
|
return `${agentCount} agent${agentCount !== 1 ? 's' : ''} active`;
|
|
@@ -267,7 +267,7 @@ export function renderSelectionModalOverlay(
|
|
|
267
267
|
|
|
268
268
|
const footerLine = createOverlayContentLine(width, layout, BORDER_FG, DEFAULT_OVERLAY_PALETTE.sectionBg);
|
|
269
269
|
const selectedItem = modal.getSelected();
|
|
270
|
-
//
|
|
270
|
+
// vocab unification: a caller-supplied primaryVerbLabel (e.g. /help's
|
|
271
271
|
// "Run", matching the slash-command palette) wins over the generic
|
|
272
272
|
// per-primaryAction default.
|
|
273
273
|
const primaryVerb = modal.primaryVerbLabel
|
|
@@ -42,7 +42,7 @@ function isClosedStatus(status: string): boolean {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
/**
|
|
45
|
-
*
|
|
45
|
+
* Closed sessions carry an optional, honest reason for WHY they
|
|
46
46
|
* closed under `metadata.closeReason` (SDK's `SharedSessionCloseReason`,
|
|
47
47
|
* 'closeReason' key — see `@pellux/goodvibes-sdk` platform/control-plane
|
|
48
48
|
* session-broker-sessions.ts's `readSessionCloseReason`). `metadata` is an
|
|
@@ -58,7 +58,7 @@ function readCloseReason(record: SharedSessionRecord): string | undefined {
|
|
|
58
58
|
/**
|
|
59
59
|
* A GC sweep closing an idle session ('idle-reaped') auto-reopens on the next
|
|
60
60
|
* heartbeat — it is NOT the same event as a deliberate user/surface close, so
|
|
61
|
-
* it must never render under the same "closed" badge
|
|
61
|
+
* it must never render under the same "closed" badge. Tolerant of
|
|
62
62
|
* records without the field (pre-feature builds, or a deliberate close).
|
|
63
63
|
*/
|
|
64
64
|
function isReapedRecord(record: SharedSessionRecord): boolean {
|
|
@@ -11,7 +11,7 @@ import { UI_TONES } from './ui-primitives.ts';
|
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* "Modified / active" accent for settings rows — cyan-green, no UI_TONES
|
|
14
|
-
* role matches it (
|
|
14
|
+
* role matches it (preserved byte-exact as a named constant).
|
|
15
15
|
*/
|
|
16
16
|
const SETTINGS_ACCENT = '#00ffcc';
|
|
17
17
|
|
|
@@ -31,6 +31,14 @@ export function formatValue(entry: SettingEntry): string {
|
|
|
31
31
|
// Array-backed settings (e.g. worktree.setup.commands) display as a
|
|
32
32
|
// comma-separated list, matching how they're edited (see worktree-setup-config.ts).
|
|
33
33
|
if (Array.isArray(val)) return val.length > 0 ? val.join(', ') : '(empty)';
|
|
34
|
+
// Object-backed settings (e.g. pricing.modelPrices) display their JSON when
|
|
35
|
+
// short, or an honest entry count — never the useless "[object Object]".
|
|
36
|
+
if (typeof val === 'object') {
|
|
37
|
+
const keys = Object.keys(val as Record<string, unknown>);
|
|
38
|
+
if (keys.length === 0) return '(none set)';
|
|
39
|
+
const json = JSON.stringify(val);
|
|
40
|
+
return json.length <= 64 ? json : `${keys.length} entr${keys.length === 1 ? 'y' : 'ies'} (Enter to edit as JSON)`;
|
|
41
|
+
}
|
|
34
42
|
return String(val);
|
|
35
43
|
}
|
|
36
44
|
|
|
@@ -57,6 +65,8 @@ export const CATEGORY_LABELS: Record<(typeof SETTINGS_CATEGORIES)[number], strin
|
|
|
57
65
|
display: 'Display',
|
|
58
66
|
ui: 'UI',
|
|
59
67
|
provider: 'Provider',
|
|
68
|
+
pricing: 'Pricing',
|
|
69
|
+
update: 'Updates',
|
|
60
70
|
subscriptions: 'Subscriptions',
|
|
61
71
|
behavior: 'Behavior',
|
|
62
72
|
storage: 'Storage',
|
|
@@ -87,7 +97,6 @@ export const CATEGORY_LABELS: Record<(typeof SETTINGS_CATEGORIES)[number], strin
|
|
|
87
97
|
release: 'Release',
|
|
88
98
|
danger: 'Danger',
|
|
89
99
|
tools: 'Tools',
|
|
90
|
-
flags: 'Advanced Features',
|
|
91
100
|
network: 'Network',
|
|
92
101
|
relay: 'Relay',
|
|
93
102
|
learning: 'Learning',
|
|
@@ -171,9 +180,9 @@ const SETTING_LABELS: Partial<Record<string, string>> = {
|
|
|
171
180
|
};
|
|
172
181
|
|
|
173
182
|
export function getSettingLabel(entry: SettingEntry): string {
|
|
174
|
-
// Feature-unit
|
|
175
|
-
//
|
|
176
|
-
if (entry.flag) return entry.flag.
|
|
183
|
+
// Feature-unit headers carry the feature's human name, not the raw
|
|
184
|
+
// enablement-key tail.
|
|
185
|
+
if (entry.flag) return entry.flag.feature.name;
|
|
177
186
|
return SETTING_LABELS[entry.setting.key] ?? entry.setting.key.replace(/^[^.]+\./, '');
|
|
178
187
|
}
|
|
179
188
|
|