@bramblex/codex-workbench 0.1.20 → 0.1.21

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bramblex/codex-workbench",
3
- "version": "0.1.20",
3
+ "version": "0.1.21",
4
4
  "description": "Terminal workbench for browsing and managing local and SSH Codex sessions.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -6,13 +6,23 @@ const blessed = require('blessed');
6
6
  const { createChildDirectory, directoryNameError, listDirectories } = require('../model/directories');
7
7
 
8
8
  const DEFAULT_HELP = '↑/↓ move ←/h parent →/l child n new directory Enter choose selected Esc/q cancel';
9
+ const color = (hex) => blessed.colors.match(hex);
10
+
11
+ const FALLBACK_THEME = {
12
+ bg: color('#101216'),
13
+ surface: color('#151a21'),
14
+ text: color('#e5e7eb'),
15
+ textInverse: color('#0b0f14'),
16
+ project: color('#22c55e'),
17
+ danger: color('#f87171'),
18
+ };
9
19
 
10
20
  const DEFAULT_OPS = {
11
21
  listDirectories: (dir) => listDirectories(dir),
12
22
  createDirectory: (parent, name) => createChildDirectory(parent, name),
13
23
  };
14
24
 
15
- function createDirectoryPicker({ screen, askInput, focusOnClose, truncate }) {
25
+ function createDirectoryPicker({ screen, askInput, focusOnClose, theme = FALLBACK_THEME, truncate }) {
16
26
  const list = blessed.list({
17
27
  parent: screen,
18
28
  label: ' Choose directory ',
@@ -25,11 +35,13 @@ function createDirectoryPicker({ screen, askInput, focusOnClose, truncate }) {
25
35
  mouse: true,
26
36
  keys: true,
27
37
  vi: false,
28
- scrollbar: { ch: ' ', track: { bg: 'black' }, style: { bg: 'green' } },
38
+ scrollbar: { ch: ' ', track: { bg: theme.surface }, style: { bg: theme.project } },
29
39
  style: {
30
- border: { fg: 'green' },
31
- selected: { fg: 'black', bg: 'green', bold: true },
32
- item: { fg: 'white' },
40
+ bg: theme.bg,
41
+ border: { fg: theme.project, bg: theme.bg },
42
+ label: { fg: theme.project, bg: theme.bg },
43
+ selected: { fg: theme.textInverse, bg: theme.project, bold: true },
44
+ item: { fg: theme.text, bg: theme.bg },
33
45
  },
34
46
  });
35
47
 
@@ -42,7 +54,12 @@ function createDirectoryPicker({ screen, askInput, focusOnClose, truncate }) {
42
54
  border: 'line',
43
55
  padding: { left: 1, right: 1 },
44
56
  content: DEFAULT_HELP,
45
- style: { border: { fg: 'green' }, fg: 'white', bg: 'black' },
57
+ style: {
58
+ bg: theme.surface,
59
+ border: { fg: theme.project, bg: theme.surface },
60
+ fg: theme.text,
61
+ label: { fg: theme.project, bg: theme.surface },
62
+ },
46
63
  });
47
64
 
48
65
  let state = null;
@@ -51,7 +68,8 @@ function createDirectoryPicker({ screen, askInput, focusOnClose, truncate }) {
51
68
 
52
69
  const setHelp = (text = DEFAULT_HELP, isError = false) => {
53
70
  help.setContent(text);
54
- help.style.fg = isError ? 'red' : 'white';
71
+ help.style.fg = isError ? theme.danger : theme.text;
72
+ help.style.bg = theme.surface;
55
73
  };
56
74
 
57
75
  const entriesFor = (dir) => {
@@ -24,6 +24,40 @@ const { usableCwd } = require('../services/codex-runner');
24
24
  const { checkForUpdate } = require('../services/update-checker');
25
25
  const { createDirectoryPicker } = require('./directory-picker');
26
26
 
27
+ const color = (hex) => blessed.colors.match(hex);
28
+
29
+ const THEME = {
30
+ bg: color('#101216'),
31
+ surface: color('#151a21'),
32
+ surfaceRaised: color('#1f2630'),
33
+ surfaceMuted: color('#343b46'),
34
+ text: color('#e5e7eb'),
35
+ textMuted: color('#8b95a1'),
36
+ textInverse: color('#0b0f14'),
37
+ borderIdle: color('#3f4652'),
38
+ accent: color('#38bdf8'),
39
+ accentAlt: color('#2563eb'),
40
+ project: color('#22c55e'),
41
+ detail: color('#f59e0b'),
42
+ warning: color('#facc15'),
43
+ danger: color('#f87171'),
44
+ success: color('#34d399'),
45
+ tag: {
46
+ accent: 'cyan',
47
+ detail: 'yellow',
48
+ muted: 'gray',
49
+ text: 'white',
50
+ warning: 'yellow',
51
+ },
52
+ backend: {
53
+ claude: 'yellow',
54
+ codex: 'cyan',
55
+ opencode: 'green',
56
+ pi: 'magenta',
57
+ unknown: 'blue',
58
+ },
59
+ };
60
+
27
61
  async function runWorkbench() {
28
62
  if (!process.stdin.isTTY || !process.stdout.isTTY) {
29
63
  return printList(loadWorkbenchSessions().sessions);
@@ -45,6 +79,7 @@ async function runWorkbench() {
45
79
  let remoteLoading = false;
46
80
  let updateInfo = null;
47
81
  let closed = false;
82
+ let searchQuery = '';
48
83
 
49
84
  const screen = blessed.screen({
50
85
  smartCSR: true,
@@ -52,6 +87,15 @@ async function runWorkbench() {
52
87
  title: appTitle,
53
88
  });
54
89
 
90
+ blessed.box({
91
+ parent: screen,
92
+ top: 0,
93
+ left: 0,
94
+ right: 0,
95
+ bottom: 0,
96
+ style: { bg: THEME.bg },
97
+ });
98
+
55
99
  const header = blessed.box({
56
100
  parent: screen,
57
101
  top: 0,
@@ -59,7 +103,7 @@ async function runWorkbench() {
59
103
  right: 0,
60
104
  height: 3,
61
105
  padding: { left: 1, right: 1 },
62
- style: { fg: 'white', bg: 'blue' },
106
+ style: { fg: THEME.text, bg: THEME.accentAlt },
63
107
  content: appTitle,
64
108
  });
65
109
 
@@ -75,11 +119,13 @@ async function runWorkbench() {
75
119
  keys: true,
76
120
  vi: false,
77
121
  tags: true,
78
- scrollbar: { ch: ' ', track: { bg: 'black' }, style: { bg: 'green' } },
122
+ scrollbar: { ch: ' ', track: { bg: THEME.surface }, style: { bg: THEME.project } },
79
123
  style: {
80
- border: { fg: 'green' },
81
- selected: { fg: 'black', bg: 'green', bold: true },
82
- item: { fg: 'white' },
124
+ bg: THEME.bg,
125
+ border: { fg: THEME.project, bg: THEME.bg },
126
+ label: { fg: THEME.project, bg: THEME.bg },
127
+ selected: { fg: THEME.textInverse, bg: THEME.project, bold: true },
128
+ item: { fg: THEME.text, bg: THEME.bg },
83
129
  },
84
130
  });
85
131
 
@@ -95,11 +141,13 @@ async function runWorkbench() {
95
141
  keys: true,
96
142
  vi: false,
97
143
  tags: true,
98
- scrollbar: { ch: ' ', track: { bg: 'black' }, style: { bg: 'cyan' } },
144
+ scrollbar: { ch: ' ', track: { bg: THEME.surface }, style: { bg: THEME.accent } },
99
145
  style: {
100
- border: { fg: 'cyan' },
101
- selected: { fg: 'black', bg: 'cyan', bold: true },
102
- item: { fg: 'white' },
146
+ bg: THEME.bg,
147
+ border: { fg: THEME.accent, bg: THEME.bg },
148
+ label: { fg: THEME.accent, bg: THEME.bg },
149
+ selected: { fg: THEME.textInverse, bg: THEME.accent, bold: true },
150
+ item: { fg: THEME.text, bg: THEME.bg },
103
151
  },
104
152
  });
105
153
 
@@ -117,10 +165,15 @@ async function runWorkbench() {
117
165
  keys: true,
118
166
  vi: true,
119
167
  alwaysScroll: true,
120
- tags: false,
121
- parseTags: false,
122
- scrollbar: { ch: ' ', track: { bg: 'black' }, style: { bg: 'cyan' } },
123
- style: { border: { fg: 'cyan' }, fg: 'white' },
168
+ tags: true,
169
+ parseTags: true,
170
+ scrollbar: { ch: ' ', track: { bg: THEME.surface }, style: { bg: THEME.accent } },
171
+ style: {
172
+ bg: THEME.bg,
173
+ border: { fg: THEME.accent, bg: THEME.bg },
174
+ fg: THEME.text,
175
+ label: { fg: THEME.accent, bg: THEME.bg },
176
+ },
124
177
  });
125
178
 
126
179
  const status = blessed.box({
@@ -130,7 +183,7 @@ async function runWorkbench() {
130
183
  bottom: 0,
131
184
  height: 3,
132
185
  padding: { left: 1, right: 1 },
133
- style: { fg: 'white', bg: 'black' },
186
+ style: { fg: THEME.text, bg: THEME.surface },
134
187
  });
135
188
 
136
189
  const prompt = blessed.prompt({
@@ -141,7 +194,12 @@ async function runWorkbench() {
141
194
  top: 'center',
142
195
  left: 'center',
143
196
  padding: { left: 1, right: 1 },
144
- style: { border: { fg: 'yellow' }, fg: 'white', bg: 'black' },
197
+ style: {
198
+ border: { fg: THEME.warning, bg: THEME.surface },
199
+ fg: THEME.text,
200
+ bg: THEME.surface,
201
+ label: { fg: THEME.warning, bg: THEME.surface },
202
+ },
145
203
  });
146
204
 
147
205
  const question = blessed.question({
@@ -152,7 +210,12 @@ async function runWorkbench() {
152
210
  top: 'center',
153
211
  left: 'center',
154
212
  padding: { left: 1, right: 1 },
155
- style: { border: { fg: 'red' }, fg: 'white', bg: 'black' },
213
+ style: {
214
+ border: { fg: THEME.danger, bg: THEME.surface },
215
+ fg: THEME.text,
216
+ bg: THEME.surface,
217
+ label: { fg: THEME.danger, bg: THEME.surface },
218
+ },
156
219
  });
157
220
 
158
221
  const backendPicker = blessed.list({
@@ -167,10 +230,13 @@ async function runWorkbench() {
167
230
  mouse: true,
168
231
  keys: true,
169
232
  vi: false,
233
+ tags: true,
170
234
  style: {
171
- border: { fg: 'yellow' },
172
- selected: { fg: 'black', bg: 'yellow', bold: true },
173
- item: { fg: 'white' },
235
+ bg: THEME.bg,
236
+ border: { fg: THEME.warning, bg: THEME.bg },
237
+ label: { fg: THEME.warning, bg: THEME.bg },
238
+ selected: { fg: THEME.textInverse, bg: THEME.warning, bold: true },
239
+ item: { fg: THEME.text, bg: THEME.bg },
174
240
  },
175
241
  });
176
242
 
@@ -204,13 +270,35 @@ async function runWorkbench() {
204
270
  if (index !== -1) groupIndex = index;
205
271
  };
206
272
 
207
- const currentSessions = () => {
273
+ const currentGroupSessions = () => {
208
274
  const group = currentGroup();
209
275
  if (group.kind === 'all') return sessions;
210
276
  if (group.kind === 'source') return sessionsForSource(group.source.id);
211
277
  return sessions.filter((session) => session.sourceId === group.source.id && session.cwd === group.cwd);
212
278
  };
213
279
 
280
+ const normalizedSearch = () => searchQuery.trim().toLowerCase();
281
+
282
+ const sessionMatchesSearch = (session) => {
283
+ const query = normalizedSearch();
284
+ if (!query) return true;
285
+ const haystack = [
286
+ session.backend,
287
+ session.cwd,
288
+ session.first,
289
+ session.id,
290
+ session.last,
291
+ session.lastAssistant,
292
+ session.name,
293
+ session.note,
294
+ session.provider,
295
+ session.sourceLabel,
296
+ ].filter(Boolean).join('\n').toLowerCase();
297
+ return haystack.includes(query);
298
+ };
299
+
300
+ const currentSessions = () => currentGroupSessions().filter(sessionMatchesSearch);
301
+
214
302
  const selectedSession = () => currentSessions()[selected] || null;
215
303
 
216
304
  const groupDisplayName = (group) => {
@@ -226,16 +314,9 @@ async function runWorkbench() {
226
314
 
227
315
  const styledListLabel = (color, text) => `{${color}-fg}{bold}${blessed.escape(text)}{/}`;
228
316
 
229
- const backendThemes = {
230
- claude: 'yellow',
231
- codex: 'cyan',
232
- pi: 'magenta',
233
- opencode: 'green',
234
- };
235
-
236
317
  const backendLabel = (backend, width = 0) => {
237
318
  const text = backend || 'unknown';
238
- const color = backendThemes[text] || 'yellow';
319
+ const color = THEME.backend[text] || THEME.backend.unknown;
239
320
  return `{${color}-fg}{bold}${blessed.escape(text.padEnd(width))}{/}`;
240
321
  };
241
322
 
@@ -247,53 +328,58 @@ async function runWorkbench() {
247
328
  const width = Math.max(12, projectWidth - 4);
248
329
  const head = `= ${text} `;
249
330
  const line = `${head}${'='.repeat(width)}`.slice(0, width);
250
- return styledListLabel('yellow', line);
331
+ return styledListLabel(THEME.tag.warning, line);
251
332
  };
252
333
 
253
334
  const projectLabel = (group) => {
254
- if (group.kind === 'all') return styledListLabel('white', `0 All (${sessions.length})`);
335
+ if (group.kind === 'all') return styledListLabel(THEME.tag.text, `0 All (${sessions.length})`);
255
336
  if (group.kind === 'source') {
256
337
  const count = sessionsForSource(group.source.id).length;
257
338
  return machineLabel(group.source, count);
258
339
  }
259
340
  const count = sessions.filter((session) => session.sourceId === group.source.id && session.cwd === group.cwd).length;
260
341
  const base = path.basename(group.cwd) || group.cwd;
261
- return ` ${blessed.escape(`${truncate(base, Math.max(10, projectWidth - 12))} (${count})`)}`;
342
+ return `{${THEME.tag.muted}-fg} ${blessed.escape(`${truncate(base, Math.max(10, projectWidth - 12))} (${count})`)}{/}`;
262
343
  };
263
344
 
264
345
  const sessionLabel = (session) => {
265
346
  const title = session.name || session.first || session.last || '(no prompt)';
266
347
  const width = Math.max(24, (screen.width || 80) - projectWidth - 8);
267
- const backendWidth = Math.max(8, Math.min(12, String(session.backend || 'unknown').length + 2));
348
+ const backendWidth = 11;
268
349
  const time = truncate(localTime(session.updatedAt), 18).padEnd(18);
269
350
  const detailWidth = Math.max(12, width - backendWidth - 22);
270
- return `${backendLabel(session.backend, backendWidth)} ${time} ${blessed.escape(truncate(title, detailWidth))}`;
351
+ return `${backendLabel(session.backend, backendWidth)} {${THEME.tag.muted}-fg}${blessed.escape(time)}{/} ${blessed.escape(truncate(title, detailWidth))}`;
271
352
  };
272
353
 
273
354
  const detailContent = (session) => {
274
- if (!session) return 'No sessions in this project.';
355
+ if (!session) return `{${THEME.tag.muted}-fg}No sessions match this view.{/}`;
275
356
  const title = session.name || session.first || session.last || '(no prompt)';
276
357
  return [
277
- title,
358
+ `{${THEME.tag.accent}-fg}{bold}${blessed.escape(title)}{/}`,
359
+ '',
360
+ `{${THEME.tag.muted}-fg}Session{/}`,
361
+ ` backend ${session.backend || 'unknown'}`,
362
+ ` id ${session.id}`,
363
+ ` source ${session.sourceLabel || 'Local'}`,
364
+ ` cwd ${session.cwd}`,
278
365
  '',
279
- `id: ${session.id}`,
280
- `backend: ${session.backend || 'unknown'}`,
281
- `source: ${session.sourceLabel || 'Local'}`,
282
- `cwd: ${session.cwd}`,
283
- `started: ${localTime(session.startedAt)}`,
284
- `updated: ${localTime(session.updatedAt)}`,
285
- `turns: ${session.turns}`,
286
- session.note ? `note: ${session.note}` : '',
366
+ `{${THEME.tag.muted}-fg}Timeline{/}`,
367
+ ` started ${localTime(session.startedAt)}`,
368
+ ` updated ${localTime(session.updatedAt)}`,
369
+ ` turns ${session.turns}`,
370
+ session.note ? `\n{${THEME.tag.muted}-fg}Note{/}\n ${session.note}` : '',
287
371
  '',
288
- `last user: ${session.last || session.first || ''}`,
372
+ `{${THEME.tag.muted}-fg}Last user{/}`,
373
+ `${session.last || session.first || ''}`,
289
374
  '',
290
- `last assistant: ${session.lastAssistant || ''}`,
375
+ `{${THEME.tag.muted}-fg}Last assistant{/}`,
376
+ `${session.lastAssistant || ''}`,
291
377
  ].filter((line) => line !== '').join('\n');
292
378
  };
293
379
 
294
380
  const setMessage = (text, isError = false) => {
295
381
  message = text || 'Ready';
296
- status.style.fg = isError ? 'red' : 'white';
382
+ status.style.fg = isError ? THEME.danger : THEME.text;
297
383
  };
298
384
 
299
385
  const visibleSession = (session) => !session.archived;
@@ -429,7 +515,9 @@ async function runWorkbench() {
429
515
  const syncList = () => {
430
516
  const visible = currentSessions();
431
517
  const listRows = Math.max(1, (sessionsList.height || Math.floor((screen.height || 24) * 0.4)) - 2);
432
- const items = visible.length ? visible.map(sessionLabel) : ['No sessions in this project.'];
518
+ const items = visible.length
519
+ ? visible.map(sessionLabel)
520
+ : [`{${THEME.tag.muted}-fg}${searchQuery ? 'No sessions match this search.' : 'No sessions in this project.'}{/}`];
433
521
  while (items.length < listRows) items.push('');
434
522
  syncingList = true;
435
523
  sessionsList.clearItems();
@@ -445,8 +533,8 @@ async function runWorkbench() {
445
533
  const setPanelLabel = (panel, title, focused, fg) => {
446
534
  panel.setLabel(focused ? ` > ${title} ` : ` ${title} `);
447
535
  if (!panel._label) return;
448
- panel._label.style.fg = focused ? fg : 'white';
449
- panel._label.style.bg = 'default';
536
+ panel._label.style.fg = focused ? fg : THEME.textMuted;
537
+ panel._label.style.bg = THEME.bg;
450
538
  panel._label.style.bold = focused;
451
539
  };
452
540
 
@@ -455,33 +543,39 @@ async function runWorkbench() {
455
543
  const sessionsFocused = activePanel === 'sessions';
456
544
  const detailFocused = activePanel === 'details';
457
545
 
458
- projectsList.style.border.fg = projectFocused ? 'green' : 'gray';
459
- sessionsList.style.border.fg = sessionsFocused ? 'cyan' : 'gray';
460
- detailBox.style.border.fg = detailFocused ? 'yellow' : 'gray';
461
- projectsList.style.selected.bg = projectFocused ? 'green' : 'gray';
462
- projectsList.style.selected.fg = 'black';
463
- sessionsList.style.selected.bg = sessionsFocused ? 'cyan' : 'gray';
464
- sessionsList.style.selected.fg = 'black';
465
-
466
- setPanelLabel(projectsList, `Sources (${sources.length})`, projectFocused, 'green');
467
- setPanelLabel(sessionsList, 'Sessions', sessionsFocused, 'cyan');
468
- setPanelLabel(detailBox, 'Details', detailFocused, 'yellow');
546
+ projectsList.style.border.fg = projectFocused ? THEME.project : THEME.borderIdle;
547
+ projectsList.style.border.bg = THEME.bg;
548
+ sessionsList.style.border.fg = sessionsFocused ? THEME.accent : THEME.borderIdle;
549
+ sessionsList.style.border.bg = THEME.bg;
550
+ detailBox.style.border.fg = detailFocused ? THEME.detail : THEME.borderIdle;
551
+ detailBox.style.border.bg = THEME.bg;
552
+ projectsList.style.selected.bg = projectFocused ? THEME.project : THEME.surfaceMuted;
553
+ projectsList.style.selected.fg = THEME.textInverse;
554
+ sessionsList.style.selected.bg = sessionsFocused ? THEME.accent : THEME.surfaceMuted;
555
+ sessionsList.style.selected.fg = THEME.textInverse;
556
+
557
+ setPanelLabel(projectsList, `Sources (${sources.length})`, projectFocused, THEME.project);
558
+ setPanelLabel(sessionsList, searchQuery ? `Sessions / ${searchQuery}` : 'Sessions', sessionsFocused, THEME.accent);
559
+ setPanelLabel(detailBox, 'Details', detailFocused, THEME.detail);
469
560
 
470
561
  const firstLine = message || 'Ready';
562
+ const searchHelp = searchQuery ? ` search "${searchQuery}" x clear` : ' / search';
471
563
  if (projectFocused) {
472
- status.setContent(`${firstLine}\nSources: ↑/↓ select 0 all 1-9 machine [/] prev/next n new → sessions q quit`);
564
+ status.setContent(`${firstLine}\nSources: ↑/↓ select 0 all 1-9 machine [/] prev/next n new${searchHelp} → sessions q quit`);
473
565
  } else if (detailFocused) {
474
- status.setContent(`${firstLine}\nDetails: ↑/↓ scroll 1-9 machine [/] prev/next n new ← sessions q quit`);
566
+ status.setContent(`${firstLine}\nDetails: ↑/↓ scroll 1-9 machine [/] prev/next n new${searchHelp} ← sessions q quit`);
475
567
  } else {
476
- status.setContent(`${firstLine}\nSessions: ↑/↓ select Enter resume 1-9 machine [/] prev/next r rename n new d delete`);
568
+ status.setContent(`${firstLine}\nSessions: ↑/↓ select Enter resume r rename n new d delete${searchHelp} q quit`);
477
569
  }
478
570
  };
479
571
 
480
572
  const render = () => {
481
573
  applyLayout();
482
574
  const visible = currentSessions();
575
+ const groupTotal = currentGroupSessions().length;
483
576
  const updateText = updateInfo ? ` Update available: v${updateInfo.latestVersion}` : '';
484
- header.setContent(` ${appTitle}${updateText}\n ${visible.length}/${sessions.length} visible ${groupDisplayName(currentGroup())}`);
577
+ const searchText = searchQuery ? ` search: ${searchQuery}` : '';
578
+ header.setContent(` ${appTitle}${updateText}\n ${visible.length}/${groupTotal} shown ${groupDisplayName(currentGroup())}${searchText}`);
485
579
  detailBox.setContent(detailContent(selectedSession()));
486
580
  updateFocusStyles();
487
581
  screen.render();
@@ -521,7 +615,10 @@ async function runWorkbench() {
521
615
 
522
616
  backendPickerState = { backends, resolve };
523
617
  backendPicker.clearItems();
524
- backendPicker.setItems(backends.map((backend) => `${backend.id} ${backend.label || backend.id}`));
618
+ backendPicker.setItems(backends.map((backend) => {
619
+ const id = String(backend.id);
620
+ return `${backendLabel(id, 11)} ${blessed.escape(backend.label || id)}`;
621
+ }));
525
622
  backendPicker.select(0);
526
623
  backendPicker.show();
527
624
  backendPicker.setFront();
@@ -533,6 +630,7 @@ async function runWorkbench() {
533
630
  askInput,
534
631
  focusOnClose: () => focusPanel(projectsList, 'projects'),
535
632
  screen,
633
+ theme: THEME,
536
634
  truncate,
537
635
  });
538
636
 
@@ -832,6 +930,25 @@ async function runWorkbench() {
832
930
  switchSource(-1);
833
931
  });
834
932
 
933
+ screen.key(['/'], async () => {
934
+ if (promptOpen()) return;
935
+ const value = await askInput('Search', searchQuery);
936
+ if (value === null) return render();
937
+ searchQuery = value.trim();
938
+ selected = 0;
939
+ syncList();
940
+ render();
941
+ });
942
+
943
+ screen.key(['x'], () => {
944
+ if (promptOpen() || !searchQuery) return;
945
+ searchQuery = '';
946
+ selected = 0;
947
+ setMessage('Search cleared.');
948
+ syncList();
949
+ render();
950
+ });
951
+
835
952
  screen.key(['q', 'escape', 'C-c'], () => {
836
953
  if (promptOpen()) return;
837
954
  leaveScreen();