@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.
Files changed (144) hide show
  1. package/CHANGELOG.md +133 -0
  2. package/README.md +1 -1
  3. package/package.json +1 -1
  4. package/src/core/conversation-line-cache.ts +432 -0
  5. package/src/core/conversation-rendering.ts +11 -3
  6. package/src/core/conversation.ts +50 -4
  7. package/src/input/commands/cost-runtime.ts +49 -0
  8. package/src/input/commands/operator-runtime.ts +5 -1
  9. package/src/input/commands.ts +2 -0
  10. package/src/input/handler-content-actions.ts +8 -3
  11. package/src/input/handler-feed-routes.ts +12 -35
  12. package/src/input/handler-feed.ts +4 -8
  13. package/src/input/handler-shortcuts.ts +51 -0
  14. package/src/input/handler.ts +43 -6
  15. package/src/input/input-history.ts +17 -4
  16. package/src/input/keybindings.ts +48 -8
  17. package/src/input/panel-integration-actions.ts +107 -1
  18. package/src/main.ts +19 -11
  19. package/src/panels/agent-inspector-panel.ts +125 -36
  20. package/src/panels/agent-inspector-shared.ts +144 -0
  21. package/src/panels/approval-panel.ts +67 -16
  22. package/src/panels/automation-control-panel.ts +368 -124
  23. package/src/panels/base-panel.ts +1 -1
  24. package/src/panels/builtin/agent.ts +28 -37
  25. package/src/panels/builtin/development.ts +40 -32
  26. package/src/panels/builtin/knowledge.ts +15 -6
  27. package/src/panels/builtin/operations.ts +178 -117
  28. package/src/panels/builtin/session.ts +35 -9
  29. package/src/panels/builtin/shared.ts +98 -6
  30. package/src/panels/cockpit-panel.ts +232 -73
  31. package/src/panels/communication-panel.ts +58 -20
  32. package/src/panels/confirm-state.ts +8 -1
  33. package/src/panels/control-plane-panel.ts +351 -118
  34. package/src/panels/cost-tracker-panel.ts +165 -7
  35. package/src/panels/debug-panel.ts +312 -159
  36. package/src/panels/diff-panel.ts +209 -57
  37. package/src/panels/docs-panel.ts +152 -66
  38. package/src/panels/eval-panel.ts +220 -42
  39. package/src/panels/file-explorer-panel.ts +202 -122
  40. package/src/panels/file-preview-panel.ts +132 -57
  41. package/src/panels/git-panel.ts +310 -129
  42. package/src/panels/hooks-panel.ts +150 -22
  43. package/src/panels/incident-review-panel.ts +223 -35
  44. package/src/panels/index.ts +0 -4
  45. package/src/panels/intelligence-panel.ts +212 -86
  46. package/src/panels/knowledge-graph-panel.ts +461 -101
  47. package/src/panels/local-auth-panel.ts +240 -28
  48. package/src/panels/marketplace-panel.ts +193 -26
  49. package/src/panels/memory-panel.ts +78 -77
  50. package/src/panels/ops-control-panel.ts +146 -29
  51. package/src/panels/ops-strategy-panel.ts +72 -4
  52. package/src/panels/orchestration-panel.ts +231 -69
  53. package/src/panels/panel-list-panel.ts +154 -131
  54. package/src/panels/panel-manager.ts +97 -9
  55. package/src/panels/plan-dashboard-panel.ts +333 -17
  56. package/src/panels/plugins-panel.ts +184 -29
  57. package/src/panels/policy-panel.ts +210 -38
  58. package/src/panels/polish-core.ts +7 -2
  59. package/src/panels/polish.ts +23 -4
  60. package/src/panels/project-planning-answer-actions.ts +134 -0
  61. package/src/panels/project-planning-panel.ts +62 -113
  62. package/src/panels/provider-health-panel.ts +434 -518
  63. package/src/panels/provider-health-routes.ts +203 -0
  64. package/src/panels/provider-health-tracker.ts +194 -6
  65. package/src/panels/provider-health-views.ts +560 -0
  66. package/src/panels/qr-panel.ts +116 -30
  67. package/src/panels/remote-panel.ts +114 -36
  68. package/src/panels/routes-panel.ts +63 -22
  69. package/src/panels/sandbox-panel.ts +174 -42
  70. package/src/panels/scrollable-list-panel.ts +19 -135
  71. package/src/panels/security-panel.ts +133 -33
  72. package/src/panels/services-panel.ts +116 -64
  73. package/src/panels/session-browser-panel.ts +73 -2
  74. package/src/panels/session-maintenance.ts +4 -122
  75. package/src/panels/settings-sync-panel.ts +335 -72
  76. package/src/panels/skills-panel.ts +157 -89
  77. package/src/panels/subscription-panel.ts +86 -33
  78. package/src/panels/symbol-outline-panel.ts +248 -108
  79. package/src/panels/system-messages-panel.ts +114 -13
  80. package/src/panels/tasks-panel.ts +326 -139
  81. package/src/panels/thinking-panel.ts +43 -10
  82. package/src/panels/token-budget-panel.ts +194 -18
  83. package/src/panels/tool-inspector-panel.ts +144 -34
  84. package/src/panels/types.ts +40 -4
  85. package/src/panels/work-plan-panel.ts +280 -17
  86. package/src/panels/worktree-panel.ts +175 -42
  87. package/src/panels/wrfc-panel.ts +116 -24
  88. package/src/renderer/agent-detail-modal.ts +37 -8
  89. package/src/renderer/code-block.ts +58 -28
  90. package/src/renderer/context-inspector.ts +3 -6
  91. package/src/renderer/conversation-overlays.ts +25 -11
  92. package/src/renderer/conversation-surface.ts +1 -21
  93. package/src/renderer/diff-view.ts +6 -4
  94. package/src/renderer/footer-tips.ts +41 -0
  95. package/src/renderer/fullscreen-primitives.ts +23 -17
  96. package/src/renderer/fullscreen-workspace.ts +2 -7
  97. package/src/renderer/help-overlay.ts +91 -14
  98. package/src/renderer/hint-grammar.ts +52 -0
  99. package/src/renderer/history-search-overlay.ts +10 -16
  100. package/src/renderer/layout.ts +7 -7
  101. package/src/renderer/markdown.ts +9 -1
  102. package/src/renderer/modal-factory.ts +23 -15
  103. package/src/renderer/model-picker-overlay.ts +8 -485
  104. package/src/renderer/model-workspace.ts +9 -24
  105. package/src/renderer/overlay-box.ts +23 -19
  106. package/src/renderer/process-indicator.ts +15 -25
  107. package/src/renderer/profile-picker-modal.ts +13 -14
  108. package/src/renderer/prompt-content-width.ts +16 -0
  109. package/src/renderer/search-overlay.ts +27 -6
  110. package/src/renderer/selection-modal-overlay.ts +3 -1
  111. package/src/renderer/semantic-diff.ts +1 -1
  112. package/src/renderer/session-picker-modal.ts +6 -4
  113. package/src/renderer/settings-modal-helpers.ts +10 -34
  114. package/src/renderer/settings-modal.ts +70 -10
  115. package/src/renderer/shell-surface.ts +54 -15
  116. package/src/renderer/status-glyphs.ts +11 -9
  117. package/src/renderer/surface-layout.ts +0 -12
  118. package/src/renderer/term-caps.ts +1 -1
  119. package/src/renderer/theme.ts +60 -3
  120. package/src/renderer/tool-call.ts +6 -20
  121. package/src/renderer/ui-factory.ts +48 -37
  122. package/src/renderer/ui-primitives.ts +41 -3
  123. package/src/runtime/bootstrap-shell.ts +35 -18
  124. package/src/runtime/diagnostics/panels/index.ts +0 -3
  125. package/src/runtime/render-scheduler.ts +80 -0
  126. package/src/shell/ui-openers.ts +14 -22
  127. package/src/utils/format-duration.ts +2 -2
  128. package/src/utils/splash-lines.ts +54 -5
  129. package/src/version.ts +1 -1
  130. package/src/work-plans/work-plan-store.ts +13 -0
  131. package/src/panels/agent-logs-panel.ts +0 -635
  132. package/src/panels/agent-logs-shared.ts +0 -129
  133. package/src/panels/context-visualizer-panel.ts +0 -238
  134. package/src/panels/forensics-panel.ts +0 -378
  135. package/src/panels/provider-account-snapshot.ts +0 -259
  136. package/src/panels/provider-accounts-panel.ts +0 -255
  137. package/src/panels/provider-stats-panel.ts +0 -391
  138. package/src/panels/schedule-panel.ts +0 -365
  139. package/src/panels/watchers-panel.ts +0 -213
  140. package/src/renderer/file-tree.ts +0 -153
  141. package/src/renderer/progress.ts +0 -99
  142. package/src/renderer/status-token.ts +0 -67
  143. package/src/runtime/diagnostics/panels/panel-resources.ts +0 -118
  144. package/src/runtime/diagnostics/panels/policy.ts +0 -177
@@ -7,7 +7,7 @@
7
7
  */
8
8
 
9
9
  import { ScrollableListPanel } from './scrollable-list-panel.ts';
10
- import type { Line } from '../types/grid.ts';
10
+ import { createEmptyLine, type Line } from '../types/grid.ts';
11
11
  import type { ComponentHealthMonitor } from '../runtime/perf/panel-health-monitor.ts';
12
12
  import {
13
13
  buildBodyText,
@@ -26,13 +26,24 @@ import {
26
26
  type PanelPalette,
27
27
  type PanelWorkspaceSection,
28
28
  } from './polish.ts';
29
+ import { type ConfirmState, handleConfirmInput, renderConfirmLines } from './confirm-state.ts';
29
30
  import { ConfigManager } from '@pellux/goodvibes-sdk/platform/config';
30
31
 
31
32
  const MAX_MESSAGES = 500;
32
33
 
34
+ // UI message-routing targets (ui.systemMessages / ui.operationalMessages /
35
+ // ui.wrfcMessages) all share the same enum. Cycled in-panel by s/o/w and
36
+ // persisted via ConfigManager.set (WO-137).
37
+ type RouteTarget = 'panel' | 'conversation' | 'both';
38
+ const ROUTE_CYCLE: readonly RouteTarget[] = ['panel', 'conversation', 'both'];
39
+ function nextRoute(current: string): RouteTarget {
40
+ const idx = ROUTE_CYCLE.indexOf(current as RouteTarget);
41
+ return ROUTE_CYCLE[(idx + 1 + ROUTE_CYCLE.length) % ROUTE_CYCLE.length]!;
42
+ }
43
+
44
+ // Domain accents only; the title band comes straight from
45
+ // DEFAULT_PANEL_PALETTE (WO-002).
33
46
  const C = extendPalette(DEFAULT_PANEL_PALETTE, {
34
- header: '#00ffff',
35
- headerBg: '#0f172a',
36
47
  high: '#fbbf24',
37
48
  low: '#9ca3af',
38
49
  ts: '#6b7280',
@@ -46,6 +57,10 @@ export interface SystemMessageEntry {
46
57
  priority: SystemMessagePriority;
47
58
  }
48
59
 
60
+ // One-key priority cycle (all -> high -> low -> all), applied on top of the
61
+ // existing text filter rather than replacing it.
62
+ const PRIORITY_CYCLE: ReadonlyArray<'' | SystemMessagePriority> = ['', 'high', 'low'];
63
+
49
64
  function fmtTime(ts: number): string {
50
65
  const d = new Date(ts);
51
66
  const hh = String(d.getHours()).padStart(2, '0');
@@ -57,9 +72,13 @@ function fmtTime(ts: number): string {
57
72
  export class SystemMessagesPanel extends ScrollableListPanel<SystemMessageEntry> {
58
73
  private _messages: SystemMessageEntry[] = [];
59
74
  private readonly configManager: ConfigManager;
75
+ /** '' = all priorities; set via the one-key priority cycle ('p'). */
76
+ private priorityFilter: '' | SystemMessagePriority = '';
77
+ /** c=clear backlog confirmation. */
78
+ private confirmClear: ConfirmState<'clear'> | null = null;
60
79
 
61
80
  constructor(configManager: ConfigManager, componentHealthMonitor?: ComponentHealthMonitor) {
62
- super('system-messages', 'System Messages', 'J', 'monitoring', componentHealthMonitor);
81
+ super('system-messages', 'System Messages', '', 'runtime-ops', componentHealthMonitor);
63
82
  this.configManager = configManager;
64
83
  this.filterEnabled = true;
65
84
  this.filterLabel = 'Filter messages';
@@ -69,6 +88,13 @@ export class SystemMessagesPanel extends ScrollableListPanel<SystemMessageEntry>
69
88
  return entry.text.toLowerCase().includes(q) || entry.priority.toLowerCase().includes(q);
70
89
  }
71
90
 
91
+ /** Text filter (base class) combined with the priority cycle filter. */
92
+ protected override getVisibleItems(): readonly SystemMessageEntry[] {
93
+ const base = super.getVisibleItems();
94
+ if (!this.priorityFilter) return base;
95
+ return base.filter((entry) => entry.priority === this.priorityFilter);
96
+ }
97
+
72
98
  // ---------------------------------------------------------------------------
73
99
  // ScrollableListPanel contract
74
100
  // ---------------------------------------------------------------------------
@@ -111,13 +137,21 @@ export class SystemMessagesPanel extends ScrollableListPanel<SystemMessageEntry>
111
137
  // ---------------------------------------------------------------------------
112
138
 
113
139
  push(text: string, priority: SystemMessagePriority): void {
140
+ // Follow-mode: only auto-jump to the new message when the selection was
141
+ // already at the tail of what's currently visible. Otherwise the user is
142
+ // reviewing history and a new low-priority message shouldn't yank the
143
+ // cursor out from under them.
144
+ const visibleBefore = this.getVisibleItems();
145
+ const wasAtTail = visibleBefore.length === 0 || this.selectedIndex >= visibleBefore.length - 1;
114
146
  this._messages.push({ ts: Date.now(), text, priority });
115
147
  if (this._messages.length > MAX_MESSAGES) {
116
148
  this._messages.shift();
117
149
  if (this.selectedIndex > 0) this.selectedIndex--;
118
150
  }
119
- // Auto-follow: jump to latest message
120
- this.selectedIndex = Math.max(0, this._messages.length - 1);
151
+ if (wasAtTail) {
152
+ const visibleAfter = this.getVisibleItems();
153
+ this.selectedIndex = Math.max(0, visibleAfter.length - 1);
154
+ }
121
155
  this.markDirty();
122
156
  }
123
157
 
@@ -130,9 +164,60 @@ export class SystemMessagesPanel extends ScrollableListPanel<SystemMessageEntry>
130
164
  }
131
165
 
132
166
  // ---------------------------------------------------------------------------
133
- // Input — base class handles all navigation; nothing custom here
167
+ // Input
134
168
  // ---------------------------------------------------------------------------
135
169
 
170
+ override handleInput(key: string): boolean {
171
+ const confirmResult = handleConfirmInput(this.confirmClear, key);
172
+ if (confirmResult === 'confirmed') {
173
+ this.confirmClear = null;
174
+ this._messages = [];
175
+ this.selectedIndex = 0;
176
+ this.markDirty();
177
+ return true;
178
+ }
179
+ if (confirmResult === 'cancelled') {
180
+ this.confirmClear = null;
181
+ this.markDirty();
182
+ return true;
183
+ }
184
+ if (confirmResult === 'absorbed') return true;
185
+
186
+ if (!this.filterActive && key === 'p') {
187
+ const idx = PRIORITY_CYCLE.indexOf(this.priorityFilter);
188
+ this.priorityFilter = PRIORITY_CYCLE[(idx + 1) % PRIORITY_CYCLE.length]!;
189
+ this.selectedIndex = 0;
190
+ this.markDirty();
191
+ return true;
192
+ }
193
+ if (!this.filterActive && key === 's') {
194
+ const ui = this.configManager.getRaw().ui;
195
+ this.configManager.set('ui.systemMessages', nextRoute(ui.systemMessages));
196
+ this.markDirty();
197
+ return true;
198
+ }
199
+ if (!this.filterActive && key === 'o') {
200
+ const ui = this.configManager.getRaw().ui;
201
+ this.configManager.set('ui.operationalMessages', nextRoute(ui.operationalMessages));
202
+ this.markDirty();
203
+ return true;
204
+ }
205
+ if (!this.filterActive && key === 'w') {
206
+ const ui = this.configManager.getRaw().ui;
207
+ this.configManager.set('ui.wrfcMessages', nextRoute(ui.wrfcMessages));
208
+ this.markDirty();
209
+ return true;
210
+ }
211
+ if (!this.filterActive && key === 'c') {
212
+ if (this._messages.length === 0) return false;
213
+ this.confirmClear = { subject: 'clear', label: `${this._messages.length} system message(s)`, verb: 'Clear' };
214
+ this.markDirty();
215
+ return true;
216
+ }
217
+
218
+ return super.handleInput(key);
219
+ }
220
+
136
221
  // ---------------------------------------------------------------------------
137
222
  // Render — multi-section layout (posture + list + detail)
138
223
  // ---------------------------------------------------------------------------
@@ -143,6 +228,7 @@ export class SystemMessagesPanel extends ScrollableListPanel<SystemMessageEntry>
143
228
  const hints: Array<{ keys: string; label: string }> = [
144
229
  { keys: 'j/k', label: 'scroll' },
145
230
  { keys: 'g/G', label: 'jump' },
231
+ { keys: 'p', label: 'priority' },
146
232
  ];
147
233
  if (this.filterActive) {
148
234
  hints.push({ keys: 'Esc', label: 'clear filter' });
@@ -150,12 +236,24 @@ export class SystemMessagesPanel extends ScrollableListPanel<SystemMessageEntry>
150
236
  hints.push({ keys: '/', label: 'edit filter' }, { keys: 'Esc', label: 'clear filter' });
151
237
  } else {
152
238
  hints.push({ keys: '/', label: 'filter' });
239
+ if (this._messages.length > 0) hints.push({ keys: 'c', label: 'clear' });
153
240
  }
154
241
  return hints;
155
242
  }
156
243
 
157
244
  override render(width: number, height: number): Line[] {
158
245
  return this.trackedRender(() => {
246
+ if (this.confirmClear) {
247
+ this.needsRender = false;
248
+ const lines = buildPanelWorkspace(width, height, {
249
+ title: 'System Messages',
250
+ sections: [{ title: 'Confirmation', lines: renderConfirmLines(width, this.confirmClear) }],
251
+ palette: C,
252
+ });
253
+ while (lines.length < height) lines.push(createEmptyLine(width));
254
+ return lines.slice(0, height);
255
+ }
256
+
159
257
  const intro = 'Operational system traffic routed out of the main conversation to reduce noise and keep runtime status reviewable.';
160
258
 
161
259
  if (this._messages.length === 0) {
@@ -198,19 +296,22 @@ export class SystemMessagesPanel extends ScrollableListPanel<SystemMessageEntry>
198
296
  ...(latest ? ([[' newest ', C.label], [`${fmtTime(latest.ts)}`, C.value]] as Array<[string, string]>) : []),
199
297
  ]),
200
298
  buildKeyValueLine(width, [
201
- { label: 'system route', value: ui.systemMessages, valueColor: C.info },
202
- { label: 'ops route', value: ui.operationalMessages, valueColor: C.info },
203
- { label: 'wrfc route', value: ui.wrfcMessages, valueColor: C.info },
299
+ { label: 'system route (s)', value: ui.systemMessages, valueColor: C.info },
300
+ { label: 'ops route (o)', value: ui.operationalMessages, valueColor: C.info },
301
+ { label: 'wrfc route (w)', value: ui.wrfcMessages, valueColor: C.info },
204
302
  ], C),
205
- buildGuidanceLine(width, '/settings', 'adjust where operational and WRFC messages render across panels and conversation', C),
303
+ buildGuidanceLine(width, '/settings', 'review the full settings surface (routing is also toggleable in-panel with s/o/w)', C),
206
304
  ];
207
305
 
208
306
  const visible = this.getVisibleItems();
209
307
  this.selectedIndex = Math.max(0, Math.min(this.selectedIndex, visible.length - 1));
210
- const selected = visible[this.selectedIndex];
308
+ const selected = this.getSelectedItem();
309
+ const noMatchReason = this.filterQuery.trim()
310
+ ? `"${this.filterQuery.trim()}"${this.priorityFilter ? ` + priority:${this.priorityFilter}` : ''}`
311
+ : `priority:${this.priorityFilter}`;
211
312
  const messageRows: Line[] = visible.length > 0
212
313
  ? visible.map((entry, index) => this.renderItem(entry, index, index === this.selectedIndex, width))
213
- : [buildPanelLine(width, [[` No messages match "${this.filterQuery.trim()}" (Esc to clear)`, C.dim]])];
314
+ : [buildPanelLine(width, [[` No messages match ${noMatchReason} (Esc to clear filter, p to reset priority)`, C.dim]])];
214
315
 
215
316
  const filterSection: PanelWorkspaceSection = { lines: [this.buildFilterLine(width)] };
216
317
  const postureSection: PanelWorkspaceSection = { lines: buildSummaryBlock(width, 'System posture', postureLines, C) };