dsh-ssh-tui 0.5.3 → 0.5.5
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/README.en.md +36 -10
- package/README.md +21 -12
- package/lib/approval-reviewer.js +56 -15
- package/lib/approval-reviewer.js.map +1 -1
- package/lib/auto-approval.js +251 -35
- package/lib/auto-approval.js.map +1 -1
- package/lib/copy-text.js +62 -0
- package/lib/copy-text.js.map +1 -0
- package/lib/dsh-compat.js +142 -1
- package/lib/dsh-compat.js.map +1 -1
- package/lib/footer.js +337 -0
- package/lib/footer.js.map +1 -0
- package/lib/i18n/en.js +62 -3
- package/lib/i18n/en.js.map +1 -1
- package/lib/i18n/zh.js +62 -3
- package/lib/i18n/zh.js.map +1 -1
- package/lib/index.js +7 -5
- package/lib/index.js.map +1 -1
- package/lib/json-args.js +30 -0
- package/lib/json-args.js.map +1 -0
- package/lib/paint.js +288 -0
- package/lib/paint.js.map +1 -0
- package/lib/picker.js +453 -66
- package/lib/picker.js.map +1 -1
- package/lib/plan.js +369 -0
- package/lib/plan.js.map +1 -0
- package/lib/quota.js +408 -0
- package/lib/quota.js.map +1 -0
- package/lib/session-index.js +80 -0
- package/lib/session-index.js.map +1 -0
- package/lib/session-list.js +210 -88
- package/lib/session-list.js.map +1 -1
- package/lib/term-text.js +953 -0
- package/lib/term-text.js.map +1 -0
- package/lib/tool-present.js +744 -0
- package/lib/tool-present.js.map +1 -0
- package/lib/transcript-types.js +6 -0
- package/lib/transcript-types.js.map +1 -0
- package/lib/tui.js +643 -3299
- package/lib/tui.js.map +1 -1
- package/lib/types/approval-reviewer.d.ts +8 -2
- package/lib/types/auto-approval.d.ts +28 -0
- package/lib/types/copy-text.d.ts +9 -0
- package/lib/types/dsh-compat.d.ts +78 -6
- package/lib/types/footer.d.ts +155 -0
- package/lib/types/json-args.d.ts +7 -0
- package/lib/types/paint.d.ts +80 -0
- package/lib/types/picker.d.ts +101 -7
- package/lib/types/plan.d.ts +80 -0
- package/lib/types/quota.d.ts +94 -0
- package/lib/types/session-index.d.ts +32 -0
- package/lib/types/session-list.d.ts +35 -2
- package/lib/types/term-text.d.ts +149 -0
- package/lib/types/tool-present.d.ts +165 -0
- package/lib/types/transcript-types.d.ts +152 -0
- package/lib/types/tui.d.ts +53 -723
- package/package.json +20 -18
package/lib/paint.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paint.js","sourceRoot":"","sources":["../src/paint.ts"],"names":[],"mappings":"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"}
|
package/lib/picker.js
CHANGED
|
@@ -1,35 +1,321 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Startup history-session picker: a
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* Startup history-session picker: a raw-mode selector shown BEFORE the main
|
|
3
|
+
* TUI mounts, so launching without an explicit session id lands on a choice
|
|
4
|
+
* instead of a fresh main screen.
|
|
5
|
+
*
|
|
6
|
+
* The list itself is not capped. The visible page is always nine rows so
|
|
7
|
+
* digits 1-9 map onto every on-screen item. Arrow keys move a highlight,
|
|
8
|
+
* typing filters by title / id / cwd, and Enter confirms the focused row.
|
|
5
9
|
*/
|
|
6
10
|
import { StringDecoder } from 'node:string_decoder';
|
|
7
|
-
import { formatSessionTime,
|
|
8
|
-
import { isEscapePrefix,
|
|
11
|
+
import { formatSessionTime, listResumableSessionsProgressive } from './session-list.js';
|
|
12
|
+
import { composePaintOutput, isEscapePrefix, pickerWindowStart } from './paint.js';
|
|
13
|
+
import { truncateToWidth } from './term-text.js';
|
|
9
14
|
import { t } from './i18n/index.js';
|
|
10
15
|
/**
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
|
|
14
|
-
|
|
16
|
+
* Visible rows in the sliding window. Locked to nine so every on-screen
|
|
17
|
+
* session has a 1-9 shortcut; remaining history is reached with ↑/↓ / filter.
|
|
18
|
+
*/
|
|
19
|
+
export const SESSION_PICKER_WINDOW = 9;
|
|
20
|
+
/** Digit shortcuts into the current window when the filter is empty. */
|
|
21
|
+
export const PICKER_QUICK_KEYS = '123456789';
|
|
22
|
+
/** Haystack used by the filter: title, id, cwd, and a live pid when attached. */
|
|
23
|
+
export function sessionSearchHaystack(session) {
|
|
24
|
+
const parts = [session.label, session.id, session.cwd];
|
|
25
|
+
if (session.attach !== undefined) {
|
|
26
|
+
parts.push(String(session.attach.pid), 'pid');
|
|
27
|
+
}
|
|
28
|
+
return parts.join('\n').toLowerCase();
|
|
29
|
+
}
|
|
30
|
+
/** Whether one session matches a whitespace-separated query (every token). */
|
|
31
|
+
export function sessionMatchesQuery(session, query) {
|
|
32
|
+
const tokens = query.trim().toLowerCase().split(/\s+/u).filter(token => token !== '');
|
|
33
|
+
if (tokens.length === 0)
|
|
34
|
+
return true;
|
|
35
|
+
const haystack = sessionSearchHaystack(session);
|
|
36
|
+
return tokens.every(token => haystack.includes(token));
|
|
37
|
+
}
|
|
38
|
+
/** Sessions still visible under the current filter, in list order. */
|
|
39
|
+
export function filterResumableSessions(sessions, query) {
|
|
40
|
+
if (query.trim() === '')
|
|
41
|
+
return [...sessions];
|
|
42
|
+
return sessions.filter(session => sessionMatchesQuery(session, query));
|
|
43
|
+
}
|
|
44
|
+
/** Keep `cursor` inside `[0, total)`. Empty lists pin to 0. */
|
|
45
|
+
export function clampPickerCursor(cursor, total) {
|
|
46
|
+
if (total <= 0)
|
|
47
|
+
return 0;
|
|
48
|
+
return Math.max(0, Math.min(total - 1, cursor));
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* How many session rows to show. Prefer a full page of nine so 1-9 always
|
|
52
|
+
* map onto every on-screen item. Shrink only when the tty cannot fit that
|
|
53
|
+
* page (title + filter + nine items + hints).
|
|
54
|
+
*/
|
|
55
|
+
export function pickerCapacity(rows) {
|
|
56
|
+
const chrome = 7;
|
|
57
|
+
const perItem = 2;
|
|
58
|
+
const available = Math.max(0, rows - chrome);
|
|
59
|
+
return Math.max(1, Math.min(SESSION_PICKER_WINDOW, Math.floor(available / perItem)));
|
|
60
|
+
}
|
|
61
|
+
/** Whether two picker states would paint the same frame. */
|
|
62
|
+
export function pickerStateUnchanged(previous, next) {
|
|
63
|
+
return previous.query === next.query
|
|
64
|
+
&& previous.cursor === next.cursor
|
|
65
|
+
&& previous.filterActive === next.filterActive
|
|
66
|
+
&& previous.sessions === next.sessions
|
|
67
|
+
&& previous.loading === next.loading;
|
|
68
|
+
}
|
|
69
|
+
function resultFor(session) {
|
|
70
|
+
return session.attach === undefined
|
|
71
|
+
? { kind: 'resume', id: session.id }
|
|
72
|
+
: { kind: 'attach', id: session.id, sock: session.attach.sock };
|
|
73
|
+
}
|
|
74
|
+
function continueWith(state) {
|
|
75
|
+
return { kind: 'continue', state };
|
|
76
|
+
}
|
|
77
|
+
function dropLastCodePoint(text) {
|
|
78
|
+
const points = Array.from(text);
|
|
79
|
+
points.pop();
|
|
80
|
+
return points.join('');
|
|
81
|
+
}
|
|
82
|
+
function retainCursor(previousId, filtered, fallback) {
|
|
83
|
+
if (previousId !== undefined) {
|
|
84
|
+
const kept = filtered.findIndex(session => session.id === previousId);
|
|
85
|
+
if (kept >= 0)
|
|
86
|
+
return kept;
|
|
87
|
+
}
|
|
88
|
+
return clampPickerCursor(fallback, filtered.length);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Apply one picker action. Pure: the TTY layer maps keystrokes onto this, and
|
|
92
|
+
* tests drive it without stdin.
|
|
93
|
+
*/
|
|
94
|
+
export function stepPicker(state, action, windowSize = SESSION_PICKER_WINDOW) {
|
|
95
|
+
const filtered = filterResumableSessions(state.sessions, state.query);
|
|
96
|
+
const focused = filtered[state.cursor];
|
|
97
|
+
switch (action.type) {
|
|
98
|
+
case 'move': {
|
|
99
|
+
const cursor = clampPickerCursor(state.cursor + action.delta, filtered.length);
|
|
100
|
+
if (cursor === state.cursor)
|
|
101
|
+
return continueWith(state);
|
|
102
|
+
return continueWith({ ...state, cursor });
|
|
103
|
+
}
|
|
104
|
+
case 'page': {
|
|
105
|
+
const cursor = clampPickerCursor(state.cursor + action.delta * Math.max(1, windowSize), filtered.length);
|
|
106
|
+
if (cursor === state.cursor)
|
|
107
|
+
return continueWith(state);
|
|
108
|
+
return continueWith({ ...state, cursor });
|
|
109
|
+
}
|
|
110
|
+
case 'home': {
|
|
111
|
+
if (state.cursor === 0)
|
|
112
|
+
return continueWith(state);
|
|
113
|
+
return continueWith({ ...state, cursor: 0 });
|
|
114
|
+
}
|
|
115
|
+
case 'end': {
|
|
116
|
+
const cursor = Math.max(0, filtered.length - 1);
|
|
117
|
+
if (cursor === state.cursor)
|
|
118
|
+
return continueWith(state);
|
|
119
|
+
return continueWith({ ...state, cursor });
|
|
120
|
+
}
|
|
121
|
+
case 'type': {
|
|
122
|
+
if (action.text === '')
|
|
123
|
+
return continueWith(state);
|
|
124
|
+
const query = `${state.query}${action.text}`;
|
|
125
|
+
const next = filterResumableSessions(state.sessions, query);
|
|
126
|
+
return continueWith({
|
|
127
|
+
...state,
|
|
128
|
+
query,
|
|
129
|
+
filterActive: true,
|
|
130
|
+
cursor: retainCursor(focused?.id, next, 0),
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
case 'backspace': {
|
|
134
|
+
if (state.query === '' && !state.filterActive)
|
|
135
|
+
return continueWith(state);
|
|
136
|
+
if (state.query === '') {
|
|
137
|
+
return continueWith({ ...state, filterActive: false });
|
|
138
|
+
}
|
|
139
|
+
const query = dropLastCodePoint(state.query);
|
|
140
|
+
const next = filterResumableSessions(state.sessions, query);
|
|
141
|
+
return continueWith({
|
|
142
|
+
...state,
|
|
143
|
+
query,
|
|
144
|
+
filterActive: query !== '',
|
|
145
|
+
cursor: retainCursor(focused?.id, next, state.cursor),
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
case 'clearQuery': {
|
|
149
|
+
if (state.query === '' && !state.filterActive)
|
|
150
|
+
return continueWith(state);
|
|
151
|
+
return continueWith({
|
|
152
|
+
...state,
|
|
153
|
+
query: '',
|
|
154
|
+
filterActive: false,
|
|
155
|
+
cursor: retainCursor(focused?.id, state.sessions, state.cursor),
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
case 'startFilter':
|
|
159
|
+
if (state.filterActive)
|
|
160
|
+
return continueWith(state);
|
|
161
|
+
return continueWith({ ...state, filterActive: true });
|
|
162
|
+
case 'quick': {
|
|
163
|
+
if (state.filterActive || state.query !== '') {
|
|
164
|
+
return stepPicker(state, { type: 'type', text: action.key }, windowSize);
|
|
165
|
+
}
|
|
166
|
+
const index = PICKER_QUICK_KEYS.indexOf(action.key);
|
|
167
|
+
if (index < 0)
|
|
168
|
+
return continueWith(state);
|
|
169
|
+
const start = pickerWindowStart(state.cursor, filtered.length, windowSize);
|
|
170
|
+
const session = filtered[start + index];
|
|
171
|
+
if (session === undefined)
|
|
172
|
+
return continueWith(state);
|
|
173
|
+
return { kind: 'done', result: resultFor(session) };
|
|
174
|
+
}
|
|
175
|
+
case 'submit': {
|
|
176
|
+
const session = filtered[state.cursor];
|
|
177
|
+
if (session === undefined)
|
|
178
|
+
return continueWith(state);
|
|
179
|
+
return { kind: 'done', result: resultFor(session) };
|
|
180
|
+
}
|
|
181
|
+
case 'new':
|
|
182
|
+
return { kind: 'done', result: { kind: 'new' } };
|
|
183
|
+
case 'escape':
|
|
184
|
+
if (state.query !== '' || state.filterActive) {
|
|
185
|
+
return stepPicker(state, { type: 'clearQuery' }, windowSize);
|
|
186
|
+
}
|
|
187
|
+
return { kind: 'done', result: null };
|
|
188
|
+
case 'cancel':
|
|
189
|
+
return { kind: 'done', result: null };
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
/** Map one decoded stdin chunk (or a complete CSI sequence) to actions. */
|
|
193
|
+
export function actionsForInput(text, state = { query: '', filterActive: false }) {
|
|
194
|
+
if (text === '\x1b[A'
|
|
195
|
+
|| text === '\x1bOA'
|
|
196
|
+
|| text === '\x10') {
|
|
197
|
+
return [{ type: 'move', delta: -1 }];
|
|
198
|
+
}
|
|
199
|
+
if (text === '\x1b[B'
|
|
200
|
+
|| text === '\x1bOB'
|
|
201
|
+
|| text === '\x0e') {
|
|
202
|
+
return [{ type: 'move', delta: 1 }];
|
|
203
|
+
}
|
|
204
|
+
if (text === '\x1b[5~')
|
|
205
|
+
return [{ type: 'page', delta: -1 }];
|
|
206
|
+
if (text === '\x1b[6~')
|
|
207
|
+
return [{ type: 'page', delta: 1 }];
|
|
208
|
+
if (text === '\x1b[H' || text === '\x1b[1~')
|
|
209
|
+
return [{ type: 'home' }];
|
|
210
|
+
if (text === '\x1b[F' || text === '\x1b[4~')
|
|
211
|
+
return [{ type: 'end' }];
|
|
212
|
+
if (text === '\x1b' || text === '\x03')
|
|
213
|
+
return [{ type: text === '\x03' ? 'cancel' : 'escape' }];
|
|
214
|
+
if (text.startsWith('\x1b'))
|
|
215
|
+
return [];
|
|
216
|
+
const filtering = state.filterActive || state.query !== '';
|
|
217
|
+
const actions = [];
|
|
218
|
+
for (const char of text) {
|
|
219
|
+
if (char === '\x03') {
|
|
220
|
+
actions.push({ type: 'cancel' });
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
if (char === '\x1b') {
|
|
224
|
+
actions.push({ type: 'escape' });
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
if (char === '\r' || char === '\n') {
|
|
228
|
+
actions.push({ type: 'submit' });
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
if (char === '\x7f' || char === '\x08') {
|
|
232
|
+
actions.push({ type: 'backspace' });
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
if (char === '\x15' || char === '\x17') {
|
|
236
|
+
actions.push({ type: 'clearQuery' });
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
if (char === '\x10') {
|
|
240
|
+
actions.push({ type: 'move', delta: -1 });
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
if (char === '\x0e') {
|
|
244
|
+
actions.push({ type: 'move', delta: 1 });
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
if (char === '\x06') {
|
|
248
|
+
actions.push({ type: 'startFilter' });
|
|
249
|
+
continue;
|
|
250
|
+
}
|
|
251
|
+
if (char === '/' && !filtering) {
|
|
252
|
+
actions.push({ type: 'startFilter' });
|
|
253
|
+
continue;
|
|
254
|
+
}
|
|
255
|
+
if (char === '0' && !filtering) {
|
|
256
|
+
actions.push({ type: 'new' });
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
if (char >= '1' && char <= '9' && !filtering) {
|
|
260
|
+
actions.push({ type: 'quick', key: char });
|
|
261
|
+
continue;
|
|
262
|
+
}
|
|
263
|
+
if (char >= ' ' && char !== '\x7f') {
|
|
264
|
+
actions.push({ type: 'type', text: char });
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
return actions;
|
|
268
|
+
}
|
|
269
|
+
/** Split a decoded stdin chunk into CSI/SS3 sequences and individual characters. */
|
|
270
|
+
export function splitPickerInput(text) {
|
|
271
|
+
const units = [];
|
|
272
|
+
let index = 0;
|
|
273
|
+
while (index < text.length) {
|
|
274
|
+
if (text[index] !== '\x1b') {
|
|
275
|
+
units.push(text[index] ?? '');
|
|
276
|
+
index += 1;
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
const rest = text.slice(index);
|
|
280
|
+
const sequence = /^(?:\x1b\[[A-DHF]|\x1b\[[1-6]~|\x1bO[A-D]|\x1b)/u.exec(rest)?.[0];
|
|
281
|
+
if (sequence === undefined) {
|
|
282
|
+
units.push('\x1b');
|
|
283
|
+
index += 1;
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
286
|
+
units.push(sequence);
|
|
287
|
+
index += sequence.length;
|
|
288
|
+
}
|
|
289
|
+
return units;
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Feed a decoded stdin chunk through the picker. CSI / SS3 sequences are one
|
|
293
|
+
* unit; ordinary text is applied character by character so a pasted "fix 2"
|
|
294
|
+
* types the digit instead of treating it as a 1-9 shortcut.
|
|
295
|
+
*/
|
|
296
|
+
export function feedPicker(state, text, windowSize = SESSION_PICKER_WINDOW) {
|
|
297
|
+
let current = state;
|
|
298
|
+
for (const unit of splitPickerInput(text)) {
|
|
299
|
+
for (const action of actionsForInput(unit, current)) {
|
|
300
|
+
const step = stepPicker(current, action, windowSize);
|
|
301
|
+
if (step.kind === 'done')
|
|
302
|
+
return step;
|
|
303
|
+
current = step.state;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
return continueWith(current);
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Show the picker and wait for a selection, a new-session request, or cancel.
|
|
310
|
+
* Restores the terminal before resolving; an AbortSignal cancels as well.
|
|
15
311
|
* @param ctx - boot context supplying sessionPersistence.
|
|
16
312
|
* @param color - whether to apply ANSI colors.
|
|
17
313
|
* @param signal - optional abort signal (fiber dispose) to cancel the picker.
|
|
18
314
|
* @returns the selection, or null when cancelled.
|
|
19
315
|
*/
|
|
20
316
|
export async function showSessionPicker(ctx, color, signal) {
|
|
21
|
-
const persistence = ctx.get('sessionPersistence');
|
|
22
|
-
if (persistence === undefined) {
|
|
23
|
-
process.stderr.write('dsh-ssh-tui: sessionPersistence service is unavailable\n');
|
|
24
|
-
return { kind: 'new' };
|
|
25
|
-
}
|
|
26
|
-
const sessions = await listResumableSessions(persistence, '');
|
|
27
317
|
if (signal?.aborted)
|
|
28
318
|
return null;
|
|
29
|
-
if (sessions.length === 0) {
|
|
30
|
-
process.stdout.write('dsh-ssh-tui: no resumable history sessions; starting a fresh session.\n');
|
|
31
|
-
return { kind: 'new' };
|
|
32
|
-
}
|
|
33
319
|
const useAltScreen = process.env.DSH_TUI_NO_ALT_SCREEN !== '1'
|
|
34
320
|
&& process.env.DSH_TUI_NO_ALT_SCREEN !== 'true';
|
|
35
321
|
const decoder = new StringDecoder('utf8');
|
|
@@ -37,35 +323,97 @@ export async function showSessionPicker(ctx, color, signal) {
|
|
|
37
323
|
process.stdin.resume();
|
|
38
324
|
process.stdout.write(`${useAltScreen ? '\x1b[?1049h' : ''}\x1b[?25l`);
|
|
39
325
|
const style = (text, code) => color ? `\x1b[${code}m${text}\x1b[0m` : text;
|
|
40
|
-
|
|
41
|
-
|
|
326
|
+
let state = { sessions: [], query: '', cursor: 0, filterActive: false, loading: true };
|
|
327
|
+
let previousRows = [];
|
|
328
|
+
let previousWidth = 0;
|
|
329
|
+
let previousHeight = 0;
|
|
330
|
+
const paintLines = (width, height) => {
|
|
331
|
+
const windowSize = pickerCapacity(height);
|
|
332
|
+
const filtered = filterResumableSessions(state.sessions, state.query);
|
|
333
|
+
const start = pickerWindowStart(state.cursor, filtered.length, windowSize);
|
|
334
|
+
const end = Math.min(filtered.length, start + windowSize);
|
|
42
335
|
const lines = [
|
|
43
336
|
style(truncateToWidth(t('picker.title'), width), '1'),
|
|
44
337
|
'─'.repeat(width),
|
|
45
338
|
];
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
339
|
+
const filtering = state.filterActive || state.query !== '';
|
|
340
|
+
const filterLine = !filtering
|
|
341
|
+
? t('picker.filterHint')
|
|
342
|
+
: t('picker.filter', { query: state.query === '' ? '▌' : `${state.query}▌` });
|
|
343
|
+
lines.push(style(truncateToWidth(filterLine, width), filtering ? '36' : '90'));
|
|
344
|
+
lines.push(style(truncateToWidth(t('picker.count', {
|
|
345
|
+
shown: filtered.length,
|
|
346
|
+
total: state.sessions.length,
|
|
347
|
+
}) + (state.loading === true ? t('picker.loading') : ''), width), '90'));
|
|
348
|
+
if (filtered.length === 0) {
|
|
349
|
+
const empty = state.loading === true
|
|
350
|
+
? t('picker.loadingList')
|
|
351
|
+
: state.query === ''
|
|
352
|
+
? t('picker.noneYet')
|
|
353
|
+
: t('picker.noMatch', { query: state.query });
|
|
354
|
+
lines.push(style(truncateToWidth(empty, width), '33'));
|
|
355
|
+
}
|
|
356
|
+
else {
|
|
357
|
+
if (start > 0) {
|
|
358
|
+
lines.push(style(truncateToWidth(t('picker.moreAbove', { count: start }), width), '90'));
|
|
359
|
+
}
|
|
360
|
+
for (let index = start; index < end; index += 1) {
|
|
361
|
+
const session = filtered[index];
|
|
362
|
+
if (session === undefined)
|
|
363
|
+
continue;
|
|
364
|
+
const quick = PICKER_QUICK_KEYS[index - start];
|
|
365
|
+
const focused = index === state.cursor;
|
|
366
|
+
const marker = focused ? '›' : ' ';
|
|
367
|
+
const key = quick ?? ' ';
|
|
368
|
+
const label = `${marker}${key} ${session.label}`;
|
|
369
|
+
lines.push(style(truncateToWidth(label, width), focused ? '1;7' : '1'));
|
|
370
|
+
const attachNote = session.attach === undefined
|
|
371
|
+
? ''
|
|
372
|
+
: t('picker.attachable', {
|
|
373
|
+
pid: session.attach.pid,
|
|
374
|
+
status: session.attach.state === 'running-detached'
|
|
375
|
+
? t('picker.attachRunning')
|
|
376
|
+
: t('picker.attachPaused'),
|
|
377
|
+
});
|
|
378
|
+
const meta = `${session.unreadable === true ? t('resume.unreadable') : ''}${formatSessionTime(session.updatedAt)} · ${session.cwd}`;
|
|
379
|
+
if (attachNote !== '') {
|
|
380
|
+
lines.push(` ${style(truncateToWidth(attachNote, Math.max(1, width - 3)), '32')}`);
|
|
381
|
+
}
|
|
382
|
+
lines.push(` ${style(truncateToWidth(meta, Math.max(1, width - 3)), '90')}`);
|
|
383
|
+
}
|
|
384
|
+
if (end < filtered.length) {
|
|
385
|
+
lines.push(style(truncateToWidth(t('picker.moreBelow', { count: filtered.length - end }), width), '90'));
|
|
386
|
+
}
|
|
68
387
|
}
|
|
388
|
+
lines.push('');
|
|
389
|
+
lines.push(style(truncateToWidth(filtering ? t('picker.hintFilter') : t('picker.hint'), width), '36'));
|
|
390
|
+
lines.push(filtering
|
|
391
|
+
? `${style('Esc', '36')} ${t('picker.cancel')}`
|
|
392
|
+
: `${style('0', '36')} ${t('picker.new')} · ${style('Esc', '36')} ${t('picker.cancel')}`);
|
|
393
|
+
return lines;
|
|
394
|
+
};
|
|
395
|
+
const render = (force = false) => {
|
|
396
|
+
const width = Math.max(20, process.stdout.columns || 80);
|
|
397
|
+
const height = Math.max(12, process.stdout.rows || 24);
|
|
398
|
+
const paintRows = paintLines(width, height);
|
|
399
|
+
const sizeChanged = force || width !== previousWidth || height !== previousHeight;
|
|
400
|
+
const frame = composePaintOutput({
|
|
401
|
+
width,
|
|
402
|
+
height,
|
|
403
|
+
paintRows,
|
|
404
|
+
previousRows,
|
|
405
|
+
sizeChanged,
|
|
406
|
+
chromeChanged: false,
|
|
407
|
+
chromeStart: paintRows.length,
|
|
408
|
+
cursorRow: 1,
|
|
409
|
+
cursorColumn: 1,
|
|
410
|
+
hideCursor: true,
|
|
411
|
+
});
|
|
412
|
+
if (frame !== '')
|
|
413
|
+
process.stdout.write(frame);
|
|
414
|
+
previousRows = paintRows;
|
|
415
|
+
previousWidth = width;
|
|
416
|
+
previousHeight = height;
|
|
69
417
|
};
|
|
70
418
|
return new Promise((resolve) => {
|
|
71
419
|
let done = false;
|
|
@@ -79,6 +427,7 @@ export async function showSessionPicker(ctx, color, signal) {
|
|
|
79
427
|
clearTimeout(escapeTimer);
|
|
80
428
|
signal?.removeEventListener('abort', onAbort);
|
|
81
429
|
process.stdin.removeListener('data', onData);
|
|
430
|
+
process.stdout.removeListener('resize', render);
|
|
82
431
|
try {
|
|
83
432
|
process.stdin.setRawMode(false);
|
|
84
433
|
}
|
|
@@ -92,6 +441,22 @@ export async function showSessionPicker(ctx, color, signal) {
|
|
|
92
441
|
const onAbort = () => {
|
|
93
442
|
cleanup(null);
|
|
94
443
|
};
|
|
444
|
+
const applyChunk = (text) => {
|
|
445
|
+
const windowSize = pickerCapacity(Math.max(12, process.stdout.rows || 24));
|
|
446
|
+
const previous = state;
|
|
447
|
+
const step = feedPicker(state, text, windowSize);
|
|
448
|
+
if (step.kind === 'done') {
|
|
449
|
+
cleanup(step.result);
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
state = step.state;
|
|
453
|
+
if (done)
|
|
454
|
+
return;
|
|
455
|
+
// Held ↑/↓ at the ends used to ED2-clear the whole screen every repeat.
|
|
456
|
+
if (pickerStateUnchanged(previous, state))
|
|
457
|
+
return;
|
|
458
|
+
render();
|
|
459
|
+
};
|
|
95
460
|
const onData = (chunk) => {
|
|
96
461
|
const combined = escapeBuffer + decoder.write(chunk);
|
|
97
462
|
escapeBuffer = '';
|
|
@@ -99,10 +464,10 @@ export async function showSessionPicker(ctx, color, signal) {
|
|
|
99
464
|
clearTimeout(escapeTimer);
|
|
100
465
|
escapeTimer = undefined;
|
|
101
466
|
}
|
|
102
|
-
// Ignore complete navigation / function-key sequences.
|
|
103
467
|
if (/^\x1b\[[A-DHF]$/u.test(combined)
|
|
104
468
|
|| /^\x1b\[[1-6]~$/u.test(combined)
|
|
105
469
|
|| /^\x1bO[A-D]$/u.test(combined)) {
|
|
470
|
+
applyChunk(combined);
|
|
106
471
|
return;
|
|
107
472
|
}
|
|
108
473
|
if (isEscapePrefix(combined)) {
|
|
@@ -111,8 +476,7 @@ export async function showSessionPicker(ctx, color, signal) {
|
|
|
111
476
|
escapeTimer = undefined;
|
|
112
477
|
const pending = escapeBuffer;
|
|
113
478
|
escapeBuffer = '';
|
|
114
|
-
|
|
115
|
-
cleanup(null);
|
|
479
|
+
applyChunk(pending);
|
|
116
480
|
}, 60);
|
|
117
481
|
return;
|
|
118
482
|
}
|
|
@@ -120,25 +484,7 @@ export async function showSessionPicker(ctx, color, signal) {
|
|
|
120
484
|
// Unknown / complete escape sequence — ignore rather than cancel.
|
|
121
485
|
return;
|
|
122
486
|
}
|
|
123
|
-
|
|
124
|
-
if (char === '\x03') {
|
|
125
|
-
cleanup(null);
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
if (char === '\r' || char === '\n' || char === '0') {
|
|
129
|
-
cleanup({ kind: 'new' });
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
if (char >= '1' && char <= '9') {
|
|
133
|
-
const session = sessions[Number(char) - 1];
|
|
134
|
-
if (session !== undefined) {
|
|
135
|
-
cleanup(session.attach === undefined
|
|
136
|
-
? { kind: 'resume', id: session.id }
|
|
137
|
-
: { kind: 'attach', id: session.id, sock: session.attach.sock });
|
|
138
|
-
return;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
}
|
|
487
|
+
applyChunk(combined);
|
|
142
488
|
};
|
|
143
489
|
if (signal?.aborted) {
|
|
144
490
|
cleanup(null);
|
|
@@ -146,7 +492,48 @@ export async function showSessionPicker(ctx, color, signal) {
|
|
|
146
492
|
}
|
|
147
493
|
signal?.addEventListener('abort', onAbort, { once: true });
|
|
148
494
|
process.stdin.on('data', onData);
|
|
149
|
-
render();
|
|
495
|
+
process.stdout.on('resize', () => { render(true); });
|
|
496
|
+
render(true);
|
|
497
|
+
const applyListing = (listing) => {
|
|
498
|
+
if (done)
|
|
499
|
+
return;
|
|
500
|
+
const focusedId = filterResumableSessions(state.sessions, state.query)[state.cursor]?.id;
|
|
501
|
+
const nextFiltered = filterResumableSessions(listing.sessions, state.query);
|
|
502
|
+
state = {
|
|
503
|
+
...state,
|
|
504
|
+
sessions: listing.sessions,
|
|
505
|
+
loading: listing.pending,
|
|
506
|
+
cursor: retainCursor(focusedId, nextFiltered, state.cursor),
|
|
507
|
+
};
|
|
508
|
+
render();
|
|
509
|
+
};
|
|
510
|
+
const startListing = async () => {
|
|
511
|
+
let persistence = ctx.get('sessionPersistence');
|
|
512
|
+
if (persistence === undefined) {
|
|
513
|
+
await ctx.get('loader')?.await();
|
|
514
|
+
if (done || signal?.aborted)
|
|
515
|
+
return;
|
|
516
|
+
persistence = ctx.get('sessionPersistence');
|
|
517
|
+
}
|
|
518
|
+
if (persistence === undefined) {
|
|
519
|
+
process.stderr.write(t('picker.noPersistence'));
|
|
520
|
+
cleanup({ kind: 'new' });
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
const listing = await listResumableSessionsProgressive(persistence, '', {
|
|
524
|
+
onUpdate: applyListing,
|
|
525
|
+
});
|
|
526
|
+
if (done)
|
|
527
|
+
return;
|
|
528
|
+
if (listing.complete.length === 0 && state.query === '') {
|
|
529
|
+
cleanup({ kind: 'new' });
|
|
530
|
+
process.stdout.write(t('picker.none'));
|
|
531
|
+
}
|
|
532
|
+
};
|
|
533
|
+
void startListing().catch(() => {
|
|
534
|
+
if (!done)
|
|
535
|
+
cleanup({ kind: 'new' });
|
|
536
|
+
});
|
|
150
537
|
});
|
|
151
538
|
}
|
|
152
539
|
//# sourceMappingURL=picker.js.map
|