@pellux/goodvibes-tui 0.27.0 → 0.29.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 +124 -0
- package/README.md +1 -1
- package/package.json +1 -1
- package/src/core/context-usage.ts +53 -0
- package/src/core/conversation.ts +4 -6
- package/src/core/session-recovery.ts +2 -0
- package/src/core/turn-event-wiring.ts +1 -0
- package/src/input/commands/cost-runtime.ts +49 -0
- package/src/input/commands/eval.ts +4 -3
- package/src/input/commands/operator-runtime.ts +5 -1
- package/src/input/commands.ts +2 -0
- package/src/input/handler-feed-routes.ts +60 -26
- package/src/input/handler-feed.ts +17 -8
- package/src/input/handler-picker-routes.ts +18 -13
- package/src/input/handler-shortcuts.ts +51 -0
- package/src/input/handler-ui-state.ts +4 -0
- package/src/input/handler.ts +43 -6
- package/src/input/keybindings.ts +53 -8
- package/src/input/model-picker.ts +5 -0
- package/src/input/panel-integration-actions.ts +117 -1
- package/src/main.ts +5 -2
- package/src/panels/agent-inspector-panel.ts +139 -45
- package/src/panels/agent-inspector-shared.ts +147 -3
- package/src/panels/approval-panel.ts +204 -102
- package/src/panels/automation-control-panel.ts +370 -103
- package/src/panels/base-panel.ts +26 -2
- 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 +253 -75
- package/src/panels/cockpit-read-model.ts +2 -1
- package/src/panels/communication-panel.ts +159 -56
- package/src/panels/confirm-state.ts +8 -1
- package/src/panels/control-plane-panel.ts +354 -95
- package/src/panels/cost-tracker-panel.ts +243 -57
- package/src/panels/debug-panel.ts +347 -159
- package/src/panels/diff-panel.ts +257 -82
- package/src/panels/docs-panel.ts +166 -64
- package/src/panels/eval-panel.ts +336 -109
- package/src/panels/expandable-list-panel.ts +189 -0
- package/src/panels/file-explorer-panel.ts +238 -159
- package/src/panels/file-preview-panel.ts +141 -59
- package/src/panels/git-panel.ts +360 -191
- package/src/panels/hooks-panel.ts +181 -19
- package/src/panels/incident-review-panel.ts +252 -43
- package/src/panels/index.ts +0 -4
- package/src/panels/intelligence-panel.ts +310 -103
- package/src/panels/knowledge-graph-panel.ts +480 -58
- package/src/panels/local-auth-panel.ts +251 -33
- package/src/panels/marketplace-panel.ts +219 -33
- package/src/panels/memory-panel.ts +83 -81
- package/src/panels/ops-control-panel.ts +211 -32
- package/src/panels/ops-strategy-panel.ts +131 -45
- package/src/panels/orchestration-panel.ts +259 -67
- package/src/panels/panel-list-panel.ts +184 -136
- package/src/panels/panel-manager.ts +120 -13
- package/src/panels/plan-dashboard-panel.ts +507 -74
- package/src/panels/plugins-panel.ts +227 -34
- package/src/panels/policy-panel.ts +264 -55
- package/src/panels/polish-core.ts +162 -0
- package/src/panels/polish-tables.ts +258 -0
- package/src/panels/polish.ts +67 -149
- package/src/panels/project-planning-answer-actions.ts +134 -0
- package/src/panels/project-planning-panel.ts +84 -113
- package/src/panels/provider-health-panel.ts +438 -480
- 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 +136 -38
- package/src/panels/remote-panel.ts +123 -38
- package/src/panels/routes-panel.ts +87 -24
- package/src/panels/sandbox-panel.ts +232 -65
- package/src/panels/scrollable-list-panel.ts +143 -145
- package/src/panels/security-panel.ts +204 -52
- package/src/panels/services-panel.ts +174 -76
- package/src/panels/session-browser-panel.ts +101 -6
- package/src/panels/session-maintenance.ts +4 -122
- package/src/panels/settings-sync-panel.ts +346 -68
- package/src/panels/skills-panel.ts +157 -89
- package/src/panels/subscription-panel.ts +105 -34
- package/src/panels/symbol-outline-panel.ts +287 -154
- package/src/panels/system-messages-panel.ts +172 -38
- package/src/panels/tasks-panel.ts +348 -134
- package/src/panels/thinking-panel.ts +66 -32
- package/src/panels/token-budget-panel.ts +233 -46
- package/src/panels/tool-inspector-panel.ts +170 -54
- package/src/panels/types.ts +65 -4
- package/src/panels/work-plan-panel.ts +393 -39
- package/src/panels/worktree-panel.ts +239 -61
- package/src/panels/wrfc-panel-format.ts +133 -0
- package/src/panels/wrfc-panel.ts +146 -150
- package/src/renderer/compaction-preview.ts +2 -1
- package/src/renderer/compositor.ts +46 -43
- package/src/renderer/conversation-overlays.ts +25 -11
- package/src/renderer/footer-tips.ts +41 -0
- package/src/renderer/fullscreen-primitives.ts +22 -16
- package/src/renderer/help-overlay.ts +91 -14
- package/src/renderer/hint-grammar.ts +52 -0
- package/src/renderer/layout.ts +7 -7
- package/src/renderer/modal-factory.ts +23 -15
- package/src/renderer/model-picker-overlay.ts +30 -11
- package/src/renderer/model-workspace.ts +2 -3
- package/src/renderer/overlay-box.ts +16 -10
- package/src/renderer/panel-composite.ts +7 -20
- package/src/renderer/panel-workspace-bar.ts +14 -8
- package/src/renderer/process-indicator.ts +3 -1
- package/src/renderer/progress.ts +8 -6
- package/src/renderer/search-overlay.ts +27 -6
- package/src/renderer/session-picker-modal.ts +6 -4
- package/src/renderer/settings-modal.ts +70 -10
- package/src/renderer/shell-surface.ts +41 -15
- package/src/renderer/status-glyphs.ts +11 -9
- package/src/renderer/tab-strip.ts +148 -34
- package/src/renderer/theme.ts +60 -3
- package/src/renderer/ui-factory.ts +45 -36
- package/src/renderer/ui-primitives.ts +23 -2
- package/src/runtime/bootstrap-shell.ts +35 -18
- package/src/runtime/diagnostics/panels/index.ts +0 -3
- package/src/runtime/ui/model-picker/health-enrichment.ts +3 -0
- package/src/runtime/ui/model-picker/types.ts +4 -0
- package/src/shell/ui-openers.ts +14 -22
- package/src/utils/format-duration.ts +55 -0
- package/src/utils/format-number.ts +71 -0
- package/src/utils/splash-lines.ts +44 -3
- package/src/version.ts +1 -1
- package/src/work-plans/work-plan-store.ts +13 -0
- package/src/panels/agent-logs-panel.ts +0 -666
- package/src/panels/agent-logs-shared.ts +0 -129
- package/src/panels/context-visualizer-panel.ts +0 -214
- package/src/panels/forensics-panel.ts +0 -364
- package/src/panels/panel-picker.ts +0 -106
- package/src/panels/provider-account-snapshot.ts +0 -259
- package/src/panels/provider-accounts-panel.ts +0 -218
- package/src/panels/provider-stats-panel.ts +0 -366
- package/src/panels/schedule-panel.ts +0 -342
- package/src/panels/watchers-panel.ts +0 -193
- package/src/renderer/panel-picker-overlay.ts +0 -202
- package/src/renderer/panel-tab-bar.ts +0 -69
- package/src/runtime/diagnostics/panels/panel-resources.ts +0 -118
- package/src/runtime/diagnostics/panels/policy.ts +0 -177
|
@@ -13,30 +13,26 @@
|
|
|
13
13
|
import type { Line } from '../types/grid.ts';
|
|
14
14
|
import type { MemoryRegistry } from '@pellux/goodvibes-sdk/platform/state';
|
|
15
15
|
import type { MemoryClass, MemoryRecord, MemoryReviewState } from '@pellux/goodvibes-sdk/platform/state';
|
|
16
|
-
import { ScrollableListPanel
|
|
16
|
+
import { ScrollableListPanel } from './scrollable-list-panel.ts';
|
|
17
|
+
import { truncateDisplay } from '../utils/terminal-width.ts';
|
|
17
18
|
import { type ConfirmState, handleConfirmInput, renderConfirmLines } from './confirm-state.ts';
|
|
18
19
|
import {
|
|
19
20
|
buildBodyText,
|
|
20
|
-
buildGuidanceLine,
|
|
21
21
|
buildKeyValueLine,
|
|
22
22
|
buildPanelLine,
|
|
23
23
|
buildPanelWorkspace,
|
|
24
24
|
extendPalette,
|
|
25
25
|
DEFAULT_PANEL_PALETTE,
|
|
26
26
|
} from './polish.ts';
|
|
27
|
-
import {
|
|
28
|
-
getPanelSearchFocusTransition,
|
|
29
|
-
isPanelSearchCancel,
|
|
30
|
-
} from './search-focus.ts';
|
|
31
27
|
import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
|
|
32
28
|
|
|
33
29
|
// ---------------------------------------------------------------------------
|
|
34
30
|
// Colour palette
|
|
35
31
|
// ---------------------------------------------------------------------------
|
|
36
32
|
|
|
33
|
+
// Domain accents only — one hue per memory class (base chrome: header/
|
|
34
|
+
// headerBg/selectBg come from DEFAULT_PANEL_PALETTE).
|
|
37
35
|
const C = extendPalette(DEFAULT_PANEL_PALETTE, {
|
|
38
|
-
header: '#94a3b8',
|
|
39
|
-
headerBg: '#1e293b',
|
|
40
36
|
decision: '#38bdf8',
|
|
41
37
|
constraint: '#f97316',
|
|
42
38
|
incident: '#ef4444',
|
|
@@ -46,9 +42,6 @@ const C = extendPalette(DEFAULT_PANEL_PALETTE, {
|
|
|
46
42
|
runbook: '#eab308',
|
|
47
43
|
architecture: '#60a5fa',
|
|
48
44
|
ownership: '#14b8a6',
|
|
49
|
-
selected: '#1e3a5f',
|
|
50
|
-
searchBg: '#0f172a',
|
|
51
|
-
searchFg: '#e2e8f0',
|
|
52
45
|
});
|
|
53
46
|
|
|
54
47
|
// ---------------------------------------------------------------------------
|
|
@@ -103,14 +96,13 @@ function formatConfidence(confidence: number): string {
|
|
|
103
96
|
// MemoryPanel
|
|
104
97
|
// ---------------------------------------------------------------------------
|
|
105
98
|
|
|
106
|
-
export class MemoryPanel extends
|
|
99
|
+
export class MemoryPanel extends ScrollableListPanel<MemoryRecord> {
|
|
107
100
|
private readonly registry: MemoryRegistry;
|
|
108
101
|
private filterMode: FilterMode = 'all';
|
|
109
|
-
private filterFocused = false;
|
|
110
102
|
private unsubscribe?: () => void;
|
|
111
103
|
|
|
112
|
-
//
|
|
113
|
-
private confirm: ConfirmState<{ id: string; action: 'stale' | 'contradicted' }> | null = null;
|
|
104
|
+
// Confirm state (for destructive stale/contradict/delete actions)
|
|
105
|
+
private confirm: ConfirmState<{ id: string; action: 'stale' | 'contradicted' | 'delete' }> | null = null;
|
|
114
106
|
|
|
115
107
|
// Cached records for review-mode (reviewQueue-first, same as former KnowledgePanel)
|
|
116
108
|
private reviewRecords: MemoryRecord[] = [];
|
|
@@ -118,6 +110,10 @@ export class MemoryPanel extends SearchableListPanel<MemoryRecord> {
|
|
|
118
110
|
constructor(registry: MemoryRegistry) {
|
|
119
111
|
super('memory', 'Memory', 'M', 'agent');
|
|
120
112
|
this.registry = registry;
|
|
113
|
+
// WO-153: converged modal '/' filter — only active in 'all' mode; review
|
|
114
|
+
// mode uses r/s/c/f/d single-letter actions instead of text filtering.
|
|
115
|
+
this.filterEnabled = true;
|
|
116
|
+
this.filterLabel = 'Search';
|
|
121
117
|
}
|
|
122
118
|
|
|
123
119
|
// ---------------------------------------------------------------------------
|
|
@@ -126,13 +122,11 @@ export class MemoryPanel extends SearchableListPanel<MemoryRecord> {
|
|
|
126
122
|
|
|
127
123
|
override onActivate(): void {
|
|
128
124
|
super.onActivate();
|
|
129
|
-
this.
|
|
130
|
-
this.
|
|
131
|
-
this.filterFocused = false;
|
|
125
|
+
this.filterQuery = '';
|
|
126
|
+
this.filterActive = false;
|
|
132
127
|
this.confirm = null;
|
|
133
128
|
this.refreshReviewRecords();
|
|
134
129
|
this.unsubscribe = this.registry.subscribe(() => {
|
|
135
|
-
this.invalidateFilter();
|
|
136
130
|
this.refreshReviewRecords();
|
|
137
131
|
this.markDirty();
|
|
138
132
|
});
|
|
@@ -148,16 +142,11 @@ export class MemoryPanel extends SearchableListPanel<MemoryRecord> {
|
|
|
148
142
|
}
|
|
149
143
|
|
|
150
144
|
// ---------------------------------------------------------------------------
|
|
151
|
-
//
|
|
145
|
+
// ScrollableListPanel implementation (filter is only enabled in 'all' mode)
|
|
152
146
|
// ---------------------------------------------------------------------------
|
|
153
147
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
protected matchesSearch(record: MemoryRecord, query: string): boolean {
|
|
159
|
-
const q = query.trim().toLowerCase();
|
|
160
|
-
if (!q) return true;
|
|
148
|
+
/** `q` arrives already trimmed + lower-cased from ScrollableListPanel.getVisibleItems(). */
|
|
149
|
+
protected override filterMatches(record: MemoryRecord, q: string): boolean {
|
|
161
150
|
const haystack = [
|
|
162
151
|
record.summary,
|
|
163
152
|
record.detail ?? '',
|
|
@@ -176,11 +165,11 @@ export class MemoryPanel extends SearchableListPanel<MemoryRecord> {
|
|
|
176
165
|
[' ', C.label, bg],
|
|
177
166
|
[record.reviewState.padEnd(13), reviewStateColor(record.reviewState), bg],
|
|
178
167
|
[` ${formatConfidence(record.confidence)} `, C.value, bg],
|
|
179
|
-
[record.summary
|
|
168
|
+
[truncateDisplay(record.summary, Math.max(0, width - 26)), C.value, bg],
|
|
180
169
|
]);
|
|
181
170
|
}
|
|
182
171
|
// All-mode row: scope/class + id + time + summary (matches former MemoryPanel row)
|
|
183
|
-
const bg = selected ? C.
|
|
172
|
+
const bg = selected ? C.selectBg : undefined;
|
|
184
173
|
return buildPanelLine(width, [
|
|
185
174
|
[' ', C.label, bg],
|
|
186
175
|
[`[${record.scope.slice(0, 1).toUpperCase()}/${record.cls.slice(0, 3).toUpperCase()}] `, classColor(record.cls), bg],
|
|
@@ -188,7 +177,7 @@ export class MemoryPanel extends SearchableListPanel<MemoryRecord> {
|
|
|
188
177
|
[' ', C.label, bg],
|
|
189
178
|
[fmtTime(record.createdAt), C.dim, bg],
|
|
190
179
|
[' ', C.label, bg],
|
|
191
|
-
[record.summary
|
|
180
|
+
[truncateDisplay(record.summary, Math.max(0, width - 33)), C.value, bg],
|
|
192
181
|
]);
|
|
193
182
|
}
|
|
194
183
|
|
|
@@ -196,9 +185,9 @@ export class MemoryPanel extends SearchableListPanel<MemoryRecord> {
|
|
|
196
185
|
|
|
197
186
|
protected override getEmptyStateMessage() {
|
|
198
187
|
if (this.filterMode === 'review') return 'No records in the review queue.';
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
188
|
+
// The filter-narrowed-to-empty case is handled by ScrollableListPanel's
|
|
189
|
+
// own 'No matches for "..."' message (renderList) before this is reached.
|
|
190
|
+
return ' No memory records. Use /recall add <class> <summary> to create one.';
|
|
202
191
|
}
|
|
203
192
|
|
|
204
193
|
protected override getEmptyStateActions() {
|
|
@@ -214,14 +203,15 @@ export class MemoryPanel extends SearchableListPanel<MemoryRecord> {
|
|
|
214
203
|
// ---------------------------------------------------------------------------
|
|
215
204
|
|
|
216
205
|
/**
|
|
217
|
-
*
|
|
218
|
-
* clampSelection, navigation bounds
|
|
219
|
-
*
|
|
220
|
-
*
|
|
206
|
+
* getItems() so that ScrollableListPanel infrastructure (renderList,
|
|
207
|
+
* clampSelection, navigation bounds, and — in 'all' mode — the modal '/'
|
|
208
|
+
* filter) all operate on the right data set per mode — preventing the
|
|
209
|
+
* render/action index desync where rendered list index N did not
|
|
210
|
+
* correspond to reviewRecords[N] when the two lists differ.
|
|
221
211
|
*/
|
|
222
|
-
protected
|
|
212
|
+
protected getItems(): readonly MemoryRecord[] {
|
|
223
213
|
if (this.filterMode === 'review') return this.reviewRecords;
|
|
224
|
-
return
|
|
214
|
+
return this.registry.search({ limit: 100 });
|
|
225
215
|
}
|
|
226
216
|
|
|
227
217
|
private refreshReviewRecords(): void {
|
|
@@ -229,11 +219,19 @@ export class MemoryPanel extends SearchableListPanel<MemoryRecord> {
|
|
|
229
219
|
this.clampSelection();
|
|
230
220
|
}
|
|
231
221
|
|
|
222
|
+
/** Count of records beyond the 100-record fetch cap in getItems(). */
|
|
223
|
+
private getHiddenCount(): number {
|
|
224
|
+
const total = this.registry.search({}).length;
|
|
225
|
+
return Math.max(0, total - 100);
|
|
226
|
+
}
|
|
227
|
+
|
|
232
228
|
private cycleFilter(): void {
|
|
233
229
|
const modes: FilterMode[] = ['all', 'review'];
|
|
234
230
|
const next = modes[(modes.indexOf(this.filterMode) + 1) % modes.length];
|
|
235
231
|
this.filterMode = next;
|
|
236
|
-
|
|
232
|
+
// The modal '/' filter only applies in 'all' mode — review mode uses
|
|
233
|
+
// r/s/c/f/d single-letter actions instead of text filtering.
|
|
234
|
+
this.filterEnabled = next === 'all';
|
|
237
235
|
this.refreshReviewRecords();
|
|
238
236
|
this.markDirty();
|
|
239
237
|
}
|
|
@@ -249,6 +247,16 @@ export class MemoryPanel extends SearchableListPanel<MemoryRecord> {
|
|
|
249
247
|
if (result === 'confirmed') {
|
|
250
248
|
const { id, action } = this.confirm.subject;
|
|
251
249
|
this.confirm = null;
|
|
250
|
+
if (action === 'delete') {
|
|
251
|
+
try {
|
|
252
|
+
this.registry.delete(id);
|
|
253
|
+
} catch (e) {
|
|
254
|
+
this.setError(`Delete failed: ${summarizeError(e)}`);
|
|
255
|
+
}
|
|
256
|
+
this.refreshReviewRecords();
|
|
257
|
+
this.markDirty();
|
|
258
|
+
return true;
|
|
259
|
+
}
|
|
252
260
|
const record = this.reviewRecords.find((r) => r.id === id);
|
|
253
261
|
if (record) {
|
|
254
262
|
try {
|
|
@@ -291,7 +299,9 @@ export class MemoryPanel extends SearchableListPanel<MemoryRecord> {
|
|
|
291
299
|
|
|
292
300
|
// Review-mode specific actions (r/s/c/f)
|
|
293
301
|
if (this.filterMode === 'review') {
|
|
294
|
-
|
|
302
|
+
// In review mode getItems() returns reviewRecords and filterEnabled is
|
|
303
|
+
// false, so getSelectedItem() resolves the highlighted review record.
|
|
304
|
+
const selected = this.getSelectedItem();
|
|
295
305
|
|
|
296
306
|
if (key === 'enter' || key === 'return' || key === 'r') {
|
|
297
307
|
if (!selected) return false;
|
|
@@ -306,13 +316,13 @@ export class MemoryPanel extends SearchableListPanel<MemoryRecord> {
|
|
|
306
316
|
}
|
|
307
317
|
if (key === 's') {
|
|
308
318
|
if (!selected) return false;
|
|
309
|
-
this.confirm = { subject: { id: selected.id, action: 'stale' }, label: selected.summary
|
|
319
|
+
this.confirm = { subject: { id: selected.id, action: 'stale' }, label: truncateDisplay(selected.summary, 40) };
|
|
310
320
|
this.markDirty();
|
|
311
321
|
return true;
|
|
312
322
|
}
|
|
313
323
|
if (key === 'c') {
|
|
314
324
|
if (!selected) return false;
|
|
315
|
-
this.confirm = { subject: { id: selected.id, action: 'contradicted' }, label: selected.summary
|
|
325
|
+
this.confirm = { subject: { id: selected.id, action: 'contradicted' }, label: truncateDisplay(selected.summary, 40) };
|
|
316
326
|
this.markDirty();
|
|
317
327
|
return true;
|
|
318
328
|
}
|
|
@@ -327,46 +337,35 @@ export class MemoryPanel extends SearchableListPanel<MemoryRecord> {
|
|
|
327
337
|
this.markDirty();
|
|
328
338
|
return true;
|
|
329
339
|
}
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
const items = this.getItems();
|
|
336
|
-
const transition = getPanelSearchFocusTransition(key, { selectedIndex: this.selectedIndex, itemCount: items.length });
|
|
337
|
-
if (transition === 'focus-list') {
|
|
338
|
-
this.filterFocused = false;
|
|
339
|
-
this.markDirty();
|
|
340
|
-
return true;
|
|
341
|
-
}
|
|
342
|
-
if (isPanelSearchCancel(key)) {
|
|
343
|
-
this.filterFocused = false;
|
|
344
|
-
return super.handleInput(key);
|
|
345
|
-
}
|
|
346
|
-
return super.handleInput(key);
|
|
340
|
+
if (key === 'd') {
|
|
341
|
+
if (!selected) return false;
|
|
342
|
+
this.confirm = { subject: { id: selected.id, action: 'delete' }, label: truncateDisplay(selected.summary, 40) };
|
|
343
|
+
this.markDirty();
|
|
344
|
+
return true;
|
|
347
345
|
}
|
|
346
|
+
}
|
|
348
347
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
348
|
+
// All-mode: 'r' reload and 'd' delete are single-letter actions that
|
|
349
|
+
// coexist with the modal '/' filter — they only fire outside filter
|
|
350
|
+
// mode, so they remain typeable into the filter query while it's active.
|
|
351
|
+
if (this.filterMode === 'all' && !this.filterActive) {
|
|
352
|
+
if (key === 'r') {
|
|
353
353
|
this.markDirty();
|
|
354
354
|
return true;
|
|
355
355
|
}
|
|
356
356
|
|
|
357
|
-
if (key === '
|
|
358
|
-
this.
|
|
357
|
+
if (key === 'd') {
|
|
358
|
+
const selected = this.getSelectedItem();
|
|
359
|
+
if (!selected) return false;
|
|
360
|
+
this.confirm = { subject: { id: selected.id, action: 'delete' }, label: truncateDisplay(selected.summary, 40) };
|
|
359
361
|
this.markDirty();
|
|
360
362
|
return true;
|
|
361
363
|
}
|
|
362
364
|
}
|
|
363
365
|
|
|
364
|
-
//
|
|
365
|
-
//
|
|
366
|
-
//
|
|
367
|
-
if (this.filterMode === 'review') {
|
|
368
|
-
return ScrollableListPanel.prototype.handleInput.call(this, key);
|
|
369
|
-
}
|
|
366
|
+
// Navigation + (in 'all' mode) the modal '/' filter: delegate to
|
|
367
|
+
// ScrollableListPanel. filterEnabled is false in review mode, so '/' is
|
|
368
|
+
// inert there and single-char keys like 'j'/'k' navigate normally.
|
|
370
369
|
return super.handleInput(key);
|
|
371
370
|
}
|
|
372
371
|
|
|
@@ -402,13 +401,16 @@ export class MemoryPanel extends SearchableListPanel<MemoryRecord> {
|
|
|
402
401
|
}
|
|
403
402
|
|
|
404
403
|
private renderAllMode(width: number, height: number, filterToggleLine: Line): Line[] {
|
|
405
|
-
|
|
404
|
+
// getVisibleItems() applies the modal '/' filter (getItems() is the
|
|
405
|
+
// unfiltered 100-cap fetch); the filter input line itself is
|
|
406
|
+
// auto-injected by renderList() below (filterEnabled=true in 'all' mode).
|
|
407
|
+
const records = this.getVisibleItems();
|
|
406
408
|
const byClass = new Map<MemoryClass, number>();
|
|
407
409
|
for (const record of records) {
|
|
408
410
|
byClass.set(record.cls, (byClass.get(record.cls) ?? 0) + 1);
|
|
409
411
|
}
|
|
410
412
|
|
|
411
|
-
const
|
|
413
|
+
const hiddenCount = this.getHiddenCount();
|
|
412
414
|
|
|
413
415
|
const summaryLines: Line[] = [
|
|
414
416
|
buildKeyValueLine(width, [
|
|
@@ -419,11 +421,12 @@ export class MemoryPanel extends SearchableListPanel<MemoryRecord> {
|
|
|
419
421
|
{ label: 'runbooks', value: String(byClass.get('runbook') ?? 0), valueColor: C.runbook },
|
|
420
422
|
], C),
|
|
421
423
|
filterToggleLine,
|
|
422
|
-
|
|
423
|
-
|
|
424
|
+
...(hiddenCount > 0
|
|
425
|
+
? [buildPanelLine(width, [[` +${hiddenCount} hidden (100-record cap)`, C.dim]])]
|
|
426
|
+
: []),
|
|
424
427
|
];
|
|
425
428
|
|
|
426
|
-
const selected =
|
|
429
|
+
const selected = this.getSelectedItem();
|
|
427
430
|
const selectedLines: Line[] = [];
|
|
428
431
|
if (selected) {
|
|
429
432
|
selectedLines.push(buildKeyValueLine(width, [
|
|
@@ -449,7 +452,7 @@ export class MemoryPanel extends SearchableListPanel<MemoryRecord> {
|
|
|
449
452
|
header: summaryLines,
|
|
450
453
|
footer: [
|
|
451
454
|
...selectedLines,
|
|
452
|
-
buildPanelLine(width, [[' / search j/k or Up/Down move r reload Tab: Review Queue Esc clear search', C.dim]]),
|
|
455
|
+
buildPanelLine(width, [[' / search j/k or Up/Down move r reload d delete Tab: Review Queue Esc clear search', C.dim]]),
|
|
453
456
|
],
|
|
454
457
|
});
|
|
455
458
|
}
|
|
@@ -499,10 +502,9 @@ export class MemoryPanel extends SearchableListPanel<MemoryRecord> {
|
|
|
499
502
|
[' team ', C.label], [String(byScope.get('team') ?? 0), C.good],
|
|
500
503
|
]),
|
|
501
504
|
filterToggleLine,
|
|
502
|
-
buildGuidanceLine(width, '/recall review', 'work the stale and contradicted queue from the command surface', C),
|
|
503
505
|
];
|
|
504
506
|
|
|
505
|
-
const selectedRecord = this.
|
|
507
|
+
const selectedRecord = this.getSelectedItem();
|
|
506
508
|
const selectedLines: Line[] = [];
|
|
507
509
|
if (selectedRecord) {
|
|
508
510
|
selectedLines.push(buildPanelLine(width, [[' Selected', C.label]]));
|
|
@@ -549,7 +551,7 @@ export class MemoryPanel extends SearchableListPanel<MemoryRecord> {
|
|
|
549
551
|
header: [...classLines, ...reviewLines],
|
|
550
552
|
footer: [
|
|
551
553
|
...(selectedLines.length > 0 ? selectedLines : []),
|
|
552
|
-
buildPanelLine(width, [[' Tab: All Records Up/Down move r/Enter reviewed s stale c contradicted f fresh', C.dim]]),
|
|
554
|
+
buildPanelLine(width, [[' Tab: All Records Up/Down move r/Enter reviewed s stale c contradicted f fresh d delete', C.dim]]),
|
|
553
555
|
],
|
|
554
556
|
});
|
|
555
557
|
}
|