@bramblex/codex-workbench 0.1.20 → 0.1.22
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 +1 -1
- package/src/services/session-sources.js +1 -1
- package/src/ui/directory-picker.js +25 -7
- package/src/ui/workbench.js +184 -67
package/package.json
CHANGED
|
@@ -142,7 +142,7 @@ function runSourceSessionCommand(session, command, args) {
|
|
|
142
142
|
}
|
|
143
143
|
|
|
144
144
|
function runSourceNewSession(source, cwd, args, backend) {
|
|
145
|
-
if (!source || !source.remote) return runNewCodexSession(cwd, args,
|
|
145
|
+
if (!source || !source.remote) return runNewCodexSession(cwd, args, false, backend || defaultBackend());
|
|
146
146
|
const backendArgs = backend ? ['--backend', backend] : [];
|
|
147
147
|
const result = runRemoteCwb(source, ['new', '--cwd', cwd, ...backendArgs, ...args], { tty: true });
|
|
148
148
|
if (result.error) throw result.error;
|
|
@@ -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:
|
|
38
|
+
scrollbar: { ch: ' ', track: { bg: theme.surface }, style: { bg: theme.project } },
|
|
29
39
|
style: {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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: {
|
|
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 ?
|
|
71
|
+
help.style.fg = isError ? theme.danger : theme.text;
|
|
72
|
+
help.style.bg = theme.surface;
|
|
55
73
|
};
|
|
56
74
|
|
|
57
75
|
const entriesFor = (dir) => {
|
package/src/ui/workbench.js
CHANGED
|
@@ -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:
|
|
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:
|
|
122
|
+
scrollbar: { ch: ' ', track: { bg: THEME.surface }, style: { bg: THEME.project } },
|
|
79
123
|
style: {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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:
|
|
144
|
+
scrollbar: { ch: ' ', track: { bg: THEME.surface }, style: { bg: THEME.accent } },
|
|
99
145
|
style: {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
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:
|
|
121
|
-
parseTags:
|
|
122
|
-
scrollbar: { ch: ' ', track: { bg:
|
|
123
|
-
style: {
|
|
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:
|
|
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: {
|
|
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: {
|
|
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
|
-
|
|
172
|
-
|
|
173
|
-
|
|
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
|
|
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 =
|
|
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(
|
|
331
|
+
return styledListLabel(THEME.tag.warning, line);
|
|
251
332
|
};
|
|
252
333
|
|
|
253
334
|
const projectLabel = (group) => {
|
|
254
|
-
if (group.kind === 'all') return styledListLabel(
|
|
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 =
|
|
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
|
|
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
|
-
`
|
|
280
|
-
`
|
|
281
|
-
`
|
|
282
|
-
`
|
|
283
|
-
|
|
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
|
-
`
|
|
372
|
+
`{${THEME.tag.muted}-fg}Last user{/}`,
|
|
373
|
+
`${session.last || session.first || ''}`,
|
|
289
374
|
'',
|
|
290
|
-
`
|
|
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 ?
|
|
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
|
|
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 :
|
|
449
|
-
panel._label.style.bg =
|
|
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 ?
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
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
|
|
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
|
-
|
|
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) =>
|
|
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
|
|
|
@@ -623,16 +721,16 @@ async function runWorkbench() {
|
|
|
623
721
|
const currentProjectCwd = () => {
|
|
624
722
|
const group = currentGroup();
|
|
625
723
|
if (group.kind === 'project') return group.cwd;
|
|
724
|
+
if (group.kind === 'source' && group.source.remote) return '.';
|
|
725
|
+
if (group.kind === 'source') return process.cwd();
|
|
626
726
|
const session = selectedSession();
|
|
627
727
|
if (session && session.cwd && session.cwd !== '(unknown)') return session.cwd;
|
|
628
|
-
if (group.kind === 'source' && group.source.remote) return '.';
|
|
629
728
|
return process.cwd();
|
|
630
729
|
};
|
|
631
730
|
|
|
632
731
|
const currentSource = () => {
|
|
633
732
|
const group = currentGroup();
|
|
634
733
|
if (group.kind === 'source' || group.kind === 'project') return group.source;
|
|
635
|
-
if (activePanel === 'projects') return LOCAL_SOURCE;
|
|
636
734
|
const session = selectedSession();
|
|
637
735
|
return session ? sourceById(sources, session.sourceId) : LOCAL_SOURCE;
|
|
638
736
|
};
|
|
@@ -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();
|