@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.
Files changed (143) hide show
  1. package/CHANGELOG.md +124 -0
  2. package/README.md +1 -1
  3. package/package.json +1 -1
  4. package/src/core/context-usage.ts +53 -0
  5. package/src/core/conversation.ts +4 -6
  6. package/src/core/session-recovery.ts +2 -0
  7. package/src/core/turn-event-wiring.ts +1 -0
  8. package/src/input/commands/cost-runtime.ts +49 -0
  9. package/src/input/commands/eval.ts +4 -3
  10. package/src/input/commands/operator-runtime.ts +5 -1
  11. package/src/input/commands.ts +2 -0
  12. package/src/input/handler-feed-routes.ts +60 -26
  13. package/src/input/handler-feed.ts +17 -8
  14. package/src/input/handler-picker-routes.ts +18 -13
  15. package/src/input/handler-shortcuts.ts +51 -0
  16. package/src/input/handler-ui-state.ts +4 -0
  17. package/src/input/handler.ts +43 -6
  18. package/src/input/keybindings.ts +53 -8
  19. package/src/input/model-picker.ts +5 -0
  20. package/src/input/panel-integration-actions.ts +117 -1
  21. package/src/main.ts +5 -2
  22. package/src/panels/agent-inspector-panel.ts +139 -45
  23. package/src/panels/agent-inspector-shared.ts +147 -3
  24. package/src/panels/approval-panel.ts +204 -102
  25. package/src/panels/automation-control-panel.ts +370 -103
  26. package/src/panels/base-panel.ts +26 -2
  27. package/src/panels/builtin/agent.ts +28 -37
  28. package/src/panels/builtin/development.ts +40 -32
  29. package/src/panels/builtin/knowledge.ts +15 -6
  30. package/src/panels/builtin/operations.ts +178 -117
  31. package/src/panels/builtin/session.ts +35 -9
  32. package/src/panels/builtin/shared.ts +98 -6
  33. package/src/panels/cockpit-panel.ts +253 -75
  34. package/src/panels/cockpit-read-model.ts +2 -1
  35. package/src/panels/communication-panel.ts +159 -56
  36. package/src/panels/confirm-state.ts +8 -1
  37. package/src/panels/control-plane-panel.ts +354 -95
  38. package/src/panels/cost-tracker-panel.ts +243 -57
  39. package/src/panels/debug-panel.ts +347 -159
  40. package/src/panels/diff-panel.ts +257 -82
  41. package/src/panels/docs-panel.ts +166 -64
  42. package/src/panels/eval-panel.ts +336 -109
  43. package/src/panels/expandable-list-panel.ts +189 -0
  44. package/src/panels/file-explorer-panel.ts +238 -159
  45. package/src/panels/file-preview-panel.ts +141 -59
  46. package/src/panels/git-panel.ts +360 -191
  47. package/src/panels/hooks-panel.ts +181 -19
  48. package/src/panels/incident-review-panel.ts +252 -43
  49. package/src/panels/index.ts +0 -4
  50. package/src/panels/intelligence-panel.ts +310 -103
  51. package/src/panels/knowledge-graph-panel.ts +480 -58
  52. package/src/panels/local-auth-panel.ts +251 -33
  53. package/src/panels/marketplace-panel.ts +219 -33
  54. package/src/panels/memory-panel.ts +83 -81
  55. package/src/panels/ops-control-panel.ts +211 -32
  56. package/src/panels/ops-strategy-panel.ts +131 -45
  57. package/src/panels/orchestration-panel.ts +259 -67
  58. package/src/panels/panel-list-panel.ts +184 -136
  59. package/src/panels/panel-manager.ts +120 -13
  60. package/src/panels/plan-dashboard-panel.ts +507 -74
  61. package/src/panels/plugins-panel.ts +227 -34
  62. package/src/panels/policy-panel.ts +264 -55
  63. package/src/panels/polish-core.ts +162 -0
  64. package/src/panels/polish-tables.ts +258 -0
  65. package/src/panels/polish.ts +67 -149
  66. package/src/panels/project-planning-answer-actions.ts +134 -0
  67. package/src/panels/project-planning-panel.ts +84 -113
  68. package/src/panels/provider-health-panel.ts +438 -480
  69. package/src/panels/provider-health-routes.ts +203 -0
  70. package/src/panels/provider-health-tracker.ts +194 -6
  71. package/src/panels/provider-health-views.ts +560 -0
  72. package/src/panels/qr-panel.ts +136 -38
  73. package/src/panels/remote-panel.ts +123 -38
  74. package/src/panels/routes-panel.ts +87 -24
  75. package/src/panels/sandbox-panel.ts +232 -65
  76. package/src/panels/scrollable-list-panel.ts +143 -145
  77. package/src/panels/security-panel.ts +204 -52
  78. package/src/panels/services-panel.ts +174 -76
  79. package/src/panels/session-browser-panel.ts +101 -6
  80. package/src/panels/session-maintenance.ts +4 -122
  81. package/src/panels/settings-sync-panel.ts +346 -68
  82. package/src/panels/skills-panel.ts +157 -89
  83. package/src/panels/subscription-panel.ts +105 -34
  84. package/src/panels/symbol-outline-panel.ts +287 -154
  85. package/src/panels/system-messages-panel.ts +172 -38
  86. package/src/panels/tasks-panel.ts +348 -134
  87. package/src/panels/thinking-panel.ts +66 -32
  88. package/src/panels/token-budget-panel.ts +233 -46
  89. package/src/panels/tool-inspector-panel.ts +170 -54
  90. package/src/panels/types.ts +65 -4
  91. package/src/panels/work-plan-panel.ts +393 -39
  92. package/src/panels/worktree-panel.ts +239 -61
  93. package/src/panels/wrfc-panel-format.ts +133 -0
  94. package/src/panels/wrfc-panel.ts +146 -150
  95. package/src/renderer/compaction-preview.ts +2 -1
  96. package/src/renderer/compositor.ts +46 -43
  97. package/src/renderer/conversation-overlays.ts +25 -11
  98. package/src/renderer/footer-tips.ts +41 -0
  99. package/src/renderer/fullscreen-primitives.ts +22 -16
  100. package/src/renderer/help-overlay.ts +91 -14
  101. package/src/renderer/hint-grammar.ts +52 -0
  102. package/src/renderer/layout.ts +7 -7
  103. package/src/renderer/modal-factory.ts +23 -15
  104. package/src/renderer/model-picker-overlay.ts +30 -11
  105. package/src/renderer/model-workspace.ts +2 -3
  106. package/src/renderer/overlay-box.ts +16 -10
  107. package/src/renderer/panel-composite.ts +7 -20
  108. package/src/renderer/panel-workspace-bar.ts +14 -8
  109. package/src/renderer/process-indicator.ts +3 -1
  110. package/src/renderer/progress.ts +8 -6
  111. package/src/renderer/search-overlay.ts +27 -6
  112. package/src/renderer/session-picker-modal.ts +6 -4
  113. package/src/renderer/settings-modal.ts +70 -10
  114. package/src/renderer/shell-surface.ts +41 -15
  115. package/src/renderer/status-glyphs.ts +11 -9
  116. package/src/renderer/tab-strip.ts +148 -34
  117. package/src/renderer/theme.ts +60 -3
  118. package/src/renderer/ui-factory.ts +45 -36
  119. package/src/renderer/ui-primitives.ts +23 -2
  120. package/src/runtime/bootstrap-shell.ts +35 -18
  121. package/src/runtime/diagnostics/panels/index.ts +0 -3
  122. package/src/runtime/ui/model-picker/health-enrichment.ts +3 -0
  123. package/src/runtime/ui/model-picker/types.ts +4 -0
  124. package/src/shell/ui-openers.ts +14 -22
  125. package/src/utils/format-duration.ts +55 -0
  126. package/src/utils/format-number.ts +71 -0
  127. package/src/utils/splash-lines.ts +44 -3
  128. package/src/version.ts +1 -1
  129. package/src/work-plans/work-plan-store.ts +13 -0
  130. package/src/panels/agent-logs-panel.ts +0 -666
  131. package/src/panels/agent-logs-shared.ts +0 -129
  132. package/src/panels/context-visualizer-panel.ts +0 -214
  133. package/src/panels/forensics-panel.ts +0 -364
  134. package/src/panels/panel-picker.ts +0 -106
  135. package/src/panels/provider-account-snapshot.ts +0 -259
  136. package/src/panels/provider-accounts-panel.ts +0 -218
  137. package/src/panels/provider-stats-panel.ts +0 -366
  138. package/src/panels/schedule-panel.ts +0 -342
  139. package/src/panels/watchers-panel.ts +0 -193
  140. package/src/renderer/panel-picker-overlay.ts +0 -202
  141. package/src/renderer/panel-tab-bar.ts +0 -69
  142. package/src/runtime/diagnostics/panels/panel-resources.ts +0 -118
  143. package/src/runtime/diagnostics/panels/policy.ts +0 -177
@@ -7,15 +7,17 @@
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,
14
14
  buildEmptyState,
15
15
  buildGuidanceLine,
16
16
  buildKeyValueLine,
17
+ buildKeyboardHints,
17
18
  buildPanelListRow,
18
19
  buildPanelLine,
20
+ buildStatusBadge,
19
21
  buildSummaryBlock,
20
22
  buildPanelWorkspace,
21
23
  resolvePrimaryScrollableSection,
@@ -24,13 +26,24 @@ import {
24
26
  type PanelPalette,
25
27
  type PanelWorkspaceSection,
26
28
  } from './polish.ts';
29
+ import { type ConfirmState, handleConfirmInput, renderConfirmLines } from './confirm-state.ts';
27
30
  import { ConfigManager } from '@pellux/goodvibes-sdk/platform/config';
28
31
 
29
32
  const MAX_MESSAGES = 500;
30
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).
31
46
  const C = extendPalette(DEFAULT_PANEL_PALETTE, {
32
- header: '#00ffff',
33
- headerBg: '#0f172a',
34
47
  high: '#fbbf24',
35
48
  low: '#9ca3af',
36
49
  ts: '#6b7280',
@@ -44,6 +57,10 @@ export interface SystemMessageEntry {
44
57
  priority: SystemMessagePriority;
45
58
  }
46
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
+
47
64
  function fmtTime(ts: number): string {
48
65
  const d = new Date(ts);
49
66
  const hh = String(d.getHours()).padStart(2, '0');
@@ -55,10 +72,27 @@ function fmtTime(ts: number): string {
55
72
  export class SystemMessagesPanel extends ScrollableListPanel<SystemMessageEntry> {
56
73
  private _messages: SystemMessageEntry[] = [];
57
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;
58
79
 
59
80
  constructor(configManager: ConfigManager, componentHealthMonitor?: ComponentHealthMonitor) {
60
- super('system-messages', 'System Messages', 'J', 'monitoring', componentHealthMonitor);
81
+ super('system-messages', 'System Messages', '', 'runtime-ops', componentHealthMonitor);
61
82
  this.configManager = configManager;
83
+ this.filterEnabled = true;
84
+ this.filterLabel = 'Filter messages';
85
+ }
86
+
87
+ protected override filterMatches(entry: SystemMessageEntry, q: string): boolean {
88
+ return entry.text.toLowerCase().includes(q) || entry.priority.toLowerCase().includes(q);
89
+ }
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);
62
96
  }
63
97
 
64
98
  // ---------------------------------------------------------------------------
@@ -79,7 +113,7 @@ export class SystemMessagesPanel extends ScrollableListPanel<SystemMessageEntry>
79
113
  return buildPanelListRow(width, [
80
114
  { text: `${fmtTime(entry.ts)} `, fg: C.ts },
81
115
  {
82
- text: `${entry.priority === 'high' ? 'HIGH' : 'LOW '.padEnd(4)} `,
116
+ text: `${(entry.priority === 'high' ? 'HIGH' : 'LOW').padEnd(4)} `,
83
117
  fg: entry.priority === 'high' ? C.high : C.low,
84
118
  bold: entry.priority === 'high',
85
119
  },
@@ -103,13 +137,21 @@ export class SystemMessagesPanel extends ScrollableListPanel<SystemMessageEntry>
103
137
  // ---------------------------------------------------------------------------
104
138
 
105
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;
106
146
  this._messages.push({ ts: Date.now(), text, priority });
107
147
  if (this._messages.length > MAX_MESSAGES) {
108
148
  this._messages.shift();
109
149
  if (this.selectedIndex > 0) this.selectedIndex--;
110
150
  }
111
- // Auto-follow: jump to latest message
112
- 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
+ }
113
155
  this.markDirty();
114
156
  }
115
157
 
@@ -122,15 +164,96 @@ export class SystemMessagesPanel extends ScrollableListPanel<SystemMessageEntry>
122
164
  }
123
165
 
124
166
  // ---------------------------------------------------------------------------
125
- // Input — base class handles all navigation; nothing custom here
167
+ // Input
126
168
  // ---------------------------------------------------------------------------
127
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
+
128
221
  // ---------------------------------------------------------------------------
129
222
  // Render — multi-section layout (posture + list + detail)
130
223
  // ---------------------------------------------------------------------------
131
224
 
225
+ // Context-aware footer: navigation keys plus filter keys that reflect the
226
+ // current filter state (active typing / applied query / inactive).
227
+ private footerHints(): Array<{ keys: string; label: string }> {
228
+ const hints: Array<{ keys: string; label: string }> = [
229
+ { keys: 'j/k', label: 'scroll' },
230
+ { keys: 'g/G', label: 'jump' },
231
+ { keys: 'p', label: 'priority' },
232
+ ];
233
+ if (this.filterActive) {
234
+ hints.push({ keys: 'Esc', label: 'clear filter' });
235
+ } else if (this.filterQuery) {
236
+ hints.push({ keys: '/', label: 'edit filter' }, { keys: 'Esc', label: 'clear filter' });
237
+ } else {
238
+ hints.push({ keys: '/', label: 'filter' });
239
+ if (this._messages.length > 0) hints.push({ keys: 'c', label: 'clear' });
240
+ }
241
+ return hints;
242
+ }
243
+
132
244
  override render(width: number, height: number): Line[] {
133
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
+
134
257
  const intro = 'Operational system traffic routed out of the main conversation to reduce noise and keep runtime status reviewable.';
135
258
 
136
259
  if (this._messages.length === 0) {
@@ -151,7 +274,7 @@ export class SystemMessagesPanel extends ScrollableListPanel<SystemMessageEntry>
151
274
  ),
152
275
  }],
153
276
  footerLines: [
154
- buildPanelLine(width, [[' j/k or Up/Down scroll g/G jump low-priority system traffic lands here by default', C.dim]]),
277
+ buildPanelLine(width, [[' Low-priority system traffic lands here by default. Routing is configurable via /settings.', C.dim]]),
155
278
  ],
156
279
  palette: C,
157
280
  });
@@ -162,42 +285,54 @@ export class SystemMessagesPanel extends ScrollableListPanel<SystemMessageEntry>
162
285
  const lowCount = this._messages.length - highCount;
163
286
  this.selectedIndex = Math.min(this.selectedIndex, this._messages.length - 1);
164
287
  const ui = this.configManager.getRaw().ui;
288
+ const latest = this._messages[this._messages.length - 1];
165
289
  const postureLines = [
290
+ // Severity + recency first: high-priority count leads, newest message age follows.
291
+ buildPanelLine(width, [
292
+ [' ', C.label],
293
+ ...buildStatusBadge(highCount > 0 ? 'failed' : 'completed', 'high', { count: highCount }),
294
+ [' ', C.dim],
295
+ ...buildStatusBadge('review', 'low', { count: lowCount }),
296
+ ...(latest ? ([[' newest ', C.label], [`${fmtTime(latest.ts)}`, C.value]] as Array<[string, string]>) : []),
297
+ ]),
166
298
  buildKeyValueLine(width, [
167
- { label: 'messages', value: String(this._messages.length), valueColor: C.value },
168
- { label: 'high', value: String(highCount), valueColor: highCount > 0 ? C.high : C.dim },
169
- { label: 'low', value: String(lowCount), valueColor: lowCount > 0 ? C.low : C.dim },
170
- ], C),
171
- buildKeyValueLine(width, [
172
- { label: 'system route', value: ui.systemMessages, valueColor: C.info },
173
- { label: 'ops route', value: ui.operationalMessages, valueColor: C.info },
174
- { 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 },
175
302
  ], C),
176
- 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),
177
304
  ];
178
305
 
179
- const selected = this._messages[this.selectedIndex]!;
180
- const messageRows: Line[] = this._messages.map((entry, index) =>
181
- this.renderItem(entry, index, index === this.selectedIndex, width),
182
- );
306
+ const visible = this.getVisibleItems();
307
+ this.selectedIndex = Math.max(0, Math.min(this.selectedIndex, visible.length - 1));
308
+ const selected = this.getSelectedItem();
309
+ const noMatchReason = this.filterQuery.trim()
310
+ ? `"${this.filterQuery.trim()}"${this.priorityFilter ? ` + priority:${this.priorityFilter}` : ''}`
311
+ : `priority:${this.priorityFilter}`;
312
+ const messageRows: Line[] = visible.length > 0
313
+ ? visible.map((entry, index) => this.renderItem(entry, index, index === this.selectedIndex, width))
314
+ : [buildPanelLine(width, [[` No messages match ${noMatchReason} (Esc to clear filter, p to reset priority)`, C.dim]])];
183
315
 
316
+ const filterSection: PanelWorkspaceSection = { lines: [this.buildFilterLine(width)] };
184
317
  const postureSection: PanelWorkspaceSection = { lines: buildSummaryBlock(width, 'System posture', postureLines, C) };
185
- const detailSection: PanelWorkspaceSection = {
186
- title: 'Selected Message',
187
- lines: [
188
- buildPanelLine(width, [
189
- [' Time ', C.label],
190
- [fmtTime(selected.ts), C.value],
191
- [' Priority ', C.label],
192
- [selected.priority, selected.priority === 'high' ? C.high : C.low],
193
- ]),
194
- ...buildBodyText(width, selected.text, C, C.value),
195
- ],
196
- };
318
+ const detailSection: PanelWorkspaceSection = selected
319
+ ? {
320
+ title: 'Selected Message',
321
+ lines: [
322
+ buildPanelLine(width, [
323
+ [' Time ', C.label],
324
+ [fmtTime(selected.ts), C.value],
325
+ [' Priority ', C.label],
326
+ [selected.priority, selected.priority === 'high' ? C.high : C.low],
327
+ ]),
328
+ ...buildBodyText(width, selected.text, C, C.value),
329
+ ],
330
+ }
331
+ : { title: 'Selected Message', lines: [] };
197
332
  const messagesSection = resolvePrimaryScrollableSection(width, height, {
198
333
  intro,
199
334
  palette: C,
200
- beforeSections: [postureSection],
335
+ beforeSections: [filterSection, postureSection],
201
336
  section: {
202
337
  title: 'Timeline',
203
338
  scrollableLines: messageRows,
@@ -210,6 +345,7 @@ export class SystemMessagesPanel extends ScrollableListPanel<SystemMessageEntry>
210
345
  });
211
346
  this.scrollStart = messagesSection.scrollOffset;
212
347
  const sections: PanelWorkspaceSection[] = [
348
+ filterSection,
213
349
  postureSection,
214
350
  messagesSection.section,
215
351
  detailSection,
@@ -219,9 +355,7 @@ export class SystemMessagesPanel extends ScrollableListPanel<SystemMessageEntry>
219
355
  title: 'System Messages',
220
356
  intro,
221
357
  sections,
222
- footerLines: [
223
- buildPanelLine(width, [[' j/k or Up/Down scroll PgUp/PgDn page g/G jump', C.dim]]),
224
- ],
358
+ footerLines: [buildKeyboardHints(width, this.footerHints(), C)],
225
359
  palette: C,
226
360
  });
227
361
  return lines;