terminal-pilot 0.0.49 → 0.0.51

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 (108) hide show
  1. package/dist/assets/jetbrains-mono-400-italic.ttf +0 -0
  2. package/dist/assets/jetbrains-mono-400-normal.ttf +0 -0
  3. package/dist/assets/jetbrains-mono-700-italic.ttf +0 -0
  4. package/dist/assets/jetbrains-mono-700-normal.ttf +0 -0
  5. package/dist/cli.js +64 -32
  6. package/dist/cli.js.map +4 -4
  7. package/dist/commands/close-session.js +26 -0
  8. package/dist/commands/close-session.js.map +2 -2
  9. package/dist/commands/create-session.js +26 -0
  10. package/dist/commands/create-session.js.map +2 -2
  11. package/dist/commands/daemon-runtime.js.map +1 -1
  12. package/dist/commands/fill.js +26 -0
  13. package/dist/commands/fill.js.map +2 -2
  14. package/dist/commands/get-session.js +26 -0
  15. package/dist/commands/get-session.js.map +2 -2
  16. package/dist/commands/index.js +39 -6
  17. package/dist/commands/index.js.map +4 -4
  18. package/dist/commands/install.js +42 -3
  19. package/dist/commands/install.js.map +4 -4
  20. package/dist/commands/installer.js +16 -3
  21. package/dist/commands/installer.js.map +4 -4
  22. package/dist/commands/list-sessions.js +26 -0
  23. package/dist/commands/list-sessions.js.map +2 -2
  24. package/dist/commands/press-key.js +26 -0
  25. package/dist/commands/press-key.js.map +2 -2
  26. package/dist/commands/read-history.js +26 -0
  27. package/dist/commands/read-history.js.map +2 -2
  28. package/dist/commands/read-screen.js +26 -0
  29. package/dist/commands/read-screen.js.map +2 -2
  30. package/dist/commands/resize.js +26 -0
  31. package/dist/commands/resize.js.map +2 -2
  32. package/dist/commands/runtime.js.map +1 -1
  33. package/dist/commands/screenshot.js +26 -0
  34. package/dist/commands/screenshot.js.map +2 -2
  35. package/dist/commands/send-signal.js +26 -0
  36. package/dist/commands/send-signal.js.map +2 -2
  37. package/dist/commands/type.js +26 -0
  38. package/dist/commands/type.js.map +2 -2
  39. package/dist/commands/uninstall.js +42 -3
  40. package/dist/commands/uninstall.js.map +4 -4
  41. package/dist/commands/wait-for-exit.js +26 -0
  42. package/dist/commands/wait-for-exit.js.map +2 -2
  43. package/dist/commands/wait-for.js +26 -0
  44. package/dist/commands/wait-for.js.map +2 -2
  45. package/dist/composition.json +1 -1
  46. package/dist/testing/cli-repl.js +64 -32
  47. package/dist/testing/cli-repl.js.map +4 -4
  48. package/dist/testing/qa-cli.js +64 -32
  49. package/dist/testing/qa-cli.js.map +4 -4
  50. package/node_modules/@poe-code/user-error/README.md +56 -0
  51. package/node_modules/@poe-code/user-error/dist/index.d.ts +17 -0
  52. package/node_modules/@poe-code/user-error/dist/index.js +21 -0
  53. package/node_modules/@poe-code/user-error/package.json +24 -0
  54. package/node_modules/toolcraft-design/README.md +30 -0
  55. package/node_modules/toolcraft-design/dist/dashboard/terminal-width.d.ts +1 -0
  56. package/node_modules/toolcraft-design/dist/dashboard/terminal-width.js +18 -1
  57. package/node_modules/toolcraft-design/dist/explorer/actions.d.ts +3 -2
  58. package/node_modules/toolcraft-design/dist/explorer/actions.js +50 -4
  59. package/node_modules/toolcraft-design/dist/explorer/events.d.ts +7 -2
  60. package/node_modules/toolcraft-design/dist/explorer/filter.js +1 -1
  61. package/node_modules/toolcraft-design/dist/explorer/index.d.ts +2 -4
  62. package/node_modules/toolcraft-design/dist/explorer/index.js +1 -2
  63. package/node_modules/toolcraft-design/dist/explorer/jobs.d.ts +2 -2
  64. package/node_modules/toolcraft-design/dist/explorer/jobs.js +3 -5
  65. package/node_modules/toolcraft-design/dist/explorer/keymap.d.ts +13 -3
  66. package/node_modules/toolcraft-design/dist/explorer/keymap.js +89 -348
  67. package/node_modules/toolcraft-design/dist/explorer/layout.d.ts +1 -0
  68. package/node_modules/toolcraft-design/dist/explorer/layout.js +14 -4
  69. package/node_modules/toolcraft-design/dist/explorer/reducer.js +116 -16
  70. package/node_modules/toolcraft-design/dist/explorer/render/detail.d.ts +1 -1
  71. package/node_modules/toolcraft-design/dist/explorer/render/detail.js +45 -7
  72. package/node_modules/toolcraft-design/dist/explorer/render/footer.d.ts +1 -1
  73. package/node_modules/toolcraft-design/dist/explorer/render/footer.js +18 -3
  74. package/node_modules/toolcraft-design/dist/explorer/render/header.d.ts +1 -1
  75. package/node_modules/toolcraft-design/dist/explorer/render/header.js +6 -2
  76. package/node_modules/toolcraft-design/dist/explorer/render/index.d.ts +1 -1
  77. package/node_modules/toolcraft-design/dist/explorer/render/index.js +2 -1
  78. package/node_modules/toolcraft-design/dist/explorer/render/list.d.ts +16 -2
  79. package/node_modules/toolcraft-design/dist/explorer/render/list.js +13 -34
  80. package/node_modules/toolcraft-design/dist/explorer/render/modal.d.ts +1 -1
  81. package/node_modules/toolcraft-design/dist/explorer/render/modal.js +11 -11
  82. package/node_modules/toolcraft-design/dist/explorer/render/pane.d.ts +5 -2
  83. package/node_modules/toolcraft-design/dist/explorer/render/pane.js +9 -6
  84. package/node_modules/toolcraft-design/dist/explorer/render/test-fixtures.js +2 -3
  85. package/node_modules/toolcraft-design/dist/explorer/runtime.js +115 -73
  86. package/node_modules/toolcraft-design/dist/explorer/runtime.test-helpers.d.ts +23 -37
  87. package/node_modules/toolcraft-design/dist/explorer/runtime.test-helpers.js +32 -83
  88. package/node_modules/toolcraft-design/dist/explorer/state.d.ts +79 -8
  89. package/node_modules/toolcraft-design/dist/explorer/state.js +70 -18
  90. package/node_modules/toolcraft-design/dist/index.d.ts +8 -2
  91. package/node_modules/toolcraft-design/dist/index.js +5 -1
  92. package/node_modules/toolcraft-design/dist/screen/ansi-text.d.ts +2 -0
  93. package/node_modules/toolcraft-design/dist/screen/ansi-text.js +35 -0
  94. package/node_modules/toolcraft-design/dist/screen/screen.d.ts +40 -0
  95. package/node_modules/toolcraft-design/dist/screen/screen.js +122 -0
  96. package/node_modules/toolcraft-design/dist/screen/style.d.ts +16 -0
  97. package/node_modules/toolcraft-design/dist/screen/style.js +55 -0
  98. package/node_modules/toolcraft-design/dist/terminal/driver.d.ts +34 -0
  99. package/node_modules/toolcraft-design/dist/terminal/driver.js +65 -0
  100. package/node_modules/toolcraft-design/dist/terminal/input.d.ts +25 -0
  101. package/node_modules/toolcraft-design/dist/terminal/input.js +181 -0
  102. package/node_modules/toolcraft-design/dist/terminal/output.d.ts +10 -0
  103. package/node_modules/toolcraft-design/dist/terminal/output.js +44 -0
  104. package/package.json +2 -1
  105. package/node_modules/toolcraft-design/dist/explorer/two-pane.d.ts +0 -102
  106. package/node_modules/toolcraft-design/dist/explorer/two-pane.js +0 -510
  107. package/node_modules/toolcraft-design/dist/run-two-pane-explorer.d.ts +0 -2
  108. package/node_modules/toolcraft-design/dist/run-two-pane-explorer.js +0 -1
@@ -1,510 +0,0 @@
1
- import { ScreenBuffer, cellToAnsi } from "../dashboard/buffer.js";
2
- import { createTerminalDriver } from "../dashboard/terminal.js";
3
- const TOAST_MS = 2500;
4
- export async function runTwoPaneExplorer(config) {
5
- if (process.stdout.isTTY !== true) {
6
- throw new Error("two-pane explorer requires a TTY");
7
- }
8
- return new TwoPaneExplorerRuntime(config, createTerminalDriver()).run();
9
- }
10
- export class TwoPaneExplorerRuntime {
11
- config;
12
- driver;
13
- state;
14
- unsubscribeKeypress;
15
- unsubscribeResize;
16
- toastTimer;
17
- stopped = false;
18
- rowsRequestToken = 0;
19
- settle;
20
- constructor(config, driver) {
21
- this.config = config;
22
- this.driver = driver;
23
- const size = normalizeSize(driver.getSize());
24
- this.state = {
25
- title: config.title,
26
- panes: [
27
- initialPaneState(config.panes[0]),
28
- initialPaneState(config.panes[1])
29
- ],
30
- activePaneIndex: 0,
31
- filterFocused: false,
32
- toast: null,
33
- size
34
- };
35
- }
36
- run() {
37
- return new Promise((resolve, reject) => {
38
- this.settle = { resolve, reject };
39
- try {
40
- this.startTerminal();
41
- this.render();
42
- this.loadRows().catch((error) => this.fail(error));
43
- }
44
- catch (error) {
45
- this.fail(error);
46
- }
47
- });
48
- }
49
- startTerminal() {
50
- this.driver.enterRawMode();
51
- this.driver.enterAltScreen();
52
- this.driver.disableLineWrap();
53
- this.driver.hideCursor();
54
- this.subscribeKeypress();
55
- this.unsubscribeResize = this.driver.onResize(() => {
56
- this.state = { ...this.state, size: normalizeSize(this.driver.getSize()) };
57
- this.render();
58
- });
59
- }
60
- subscribeKeypress() {
61
- this.unsubscribeKeypress = this.driver.onKeypress((key) => {
62
- this.dispatchKey(key);
63
- });
64
- }
65
- pauseKeypress() {
66
- this.unsubscribeKeypress?.();
67
- this.unsubscribeKeypress = undefined;
68
- }
69
- async loadRows(requestToken = ++this.rowsRequestToken) {
70
- const [leftRows, rightRows] = await Promise.all([
71
- this.config.panes[0].rows(),
72
- this.config.panes[1].rows()
73
- ]);
74
- if (requestToken !== this.rowsRequestToken) {
75
- return;
76
- }
77
- this.state = {
78
- ...this.state,
79
- panes: [
80
- rowsLoaded(this.state.panes[0], leftRows),
81
- rowsLoaded(this.state.panes[1], rightRows)
82
- ]
83
- };
84
- this.render();
85
- }
86
- async refresh() {
87
- await this.config.refresh?.();
88
- await this.loadRows(++this.rowsRequestToken);
89
- }
90
- dispatchKey(key) {
91
- if (this.stopped) {
92
- return;
93
- }
94
- this.trace("key", {
95
- key: traceKey(key),
96
- filterFocused: this.state.filterFocused,
97
- pane: this.activePane().id,
98
- cursor: this.activePane().cursor,
99
- selected: this.activePane().selected.size,
100
- filter: this.activePane().filter
101
- });
102
- if (this.state.filterFocused) {
103
- this.dispatchFilterKey(key);
104
- return;
105
- }
106
- if (isQuitKey(key)) {
107
- this.exit(null);
108
- return;
109
- }
110
- if (isTabKey(key)) {
111
- this.state = {
112
- ...this.state,
113
- activePaneIndex: this.state.activePaneIndex === 0 ? 1 : 0
114
- };
115
- this.render();
116
- return;
117
- }
118
- if (key.name === "up" || key.name === "down") {
119
- this.moveCursor(key.name === "up" ? -1 : 1);
120
- return;
121
- }
122
- if (key.name === "home") {
123
- this.setCursor(0);
124
- return;
125
- }
126
- if (key.name === "end") {
127
- this.setCursor(filteredRows(this.activePane()).length - 1);
128
- return;
129
- }
130
- if (key.ch === " " || key.name === "space") {
131
- this.toggleSelection();
132
- return;
133
- }
134
- if (key.ch === "/") {
135
- this.state = {
136
- ...this.state,
137
- filterFocused: true,
138
- panes: updateActivePane(this.state, (pane) => ({ ...pane, filter: "", cursor: 0 }))
139
- };
140
- this.render();
141
- return;
142
- }
143
- const action = this.config.actions.find((candidate) => actionMatchesKey(candidate, key));
144
- if (action !== undefined) {
145
- this.runAction(action);
146
- }
147
- }
148
- dispatchFilterKey(key) {
149
- if (key.name === "escape" || key.name === "return" || key.name === "enter") {
150
- this.state = { ...this.state, filterFocused: false };
151
- this.trace("filter.submit", {
152
- key: traceKey(key),
153
- pane: this.activePane().id,
154
- filter: this.activePane().filter,
155
- rows: filteredRows(this.activePane()).length
156
- });
157
- this.render();
158
- return;
159
- }
160
- if (key.name === "backspace") {
161
- this.state = {
162
- ...this.state,
163
- panes: updateActivePane(this.state, (pane) => ({
164
- ...pane,
165
- filter: Array.from(pane.filter).slice(0, -1).join(""),
166
- cursor: 0
167
- }))
168
- };
169
- this.trace("filter.update", {
170
- key: traceKey(key),
171
- pane: this.activePane().id,
172
- filter: this.activePane().filter,
173
- rows: filteredRows(this.activePane()).length
174
- });
175
- this.render();
176
- return;
177
- }
178
- if (key.ch !== undefined && key.ch.length > 0 && key.ch !== "/") {
179
- this.state = {
180
- ...this.state,
181
- panes: updateActivePane(this.state, (pane) => ({
182
- ...pane,
183
- filter: `${pane.filter}${key.ch}`,
184
- cursor: 0
185
- }))
186
- };
187
- this.trace("filter.update", {
188
- key: traceKey(key),
189
- pane: this.activePane().id,
190
- filter: this.activePane().filter,
191
- rows: filteredRows(this.activePane()).length
192
- });
193
- this.render();
194
- }
195
- }
196
- moveCursor(delta) {
197
- const pane = this.activePane();
198
- const rows = filteredRows(pane);
199
- if (rows.length === 0) {
200
- return;
201
- }
202
- this.setCursor(Math.max(0, Math.min(rows.length - 1, pane.cursor + delta)));
203
- }
204
- setCursor(cursor) {
205
- this.state = {
206
- ...this.state,
207
- panes: updateActivePane(this.state, (pane) => ({
208
- ...pane,
209
- cursor: Math.max(0, cursor)
210
- }))
211
- };
212
- this.render();
213
- }
214
- toggleSelection() {
215
- const pane = this.activePane();
216
- const row = currentRow(pane);
217
- if (row === undefined) {
218
- return;
219
- }
220
- const selected = new Set(pane.selected);
221
- if (selected.has(row.id)) {
222
- selected.delete(row.id);
223
- }
224
- else {
225
- selected.add(row.id);
226
- }
227
- this.state = {
228
- ...this.state,
229
- panes: updateActivePane(this.state, (candidate) => ({ ...candidate, selected }))
230
- };
231
- this.trace("selection.toggle", {
232
- pane: pane.id,
233
- row: row.id,
234
- selected: selected.size,
235
- checked: selected.has(row.id),
236
- filter: pane.filter,
237
- cursor: pane.cursor
238
- });
239
- this.render();
240
- }
241
- runAction(action) {
242
- const activePane = this.activePane();
243
- const inactivePane = this.inactivePane();
244
- const row = currentRow(activePane);
245
- if (row === undefined) {
246
- this.showToast("Select an item first", "warning");
247
- return;
248
- }
249
- const selectedRows = activePane.rows.filter((candidate) => activePane.selected.has(candidate.id));
250
- const context = {
251
- activePane,
252
- inactivePane,
253
- row,
254
- rows: selectedRows.length > 0 ? selectedRows : [row],
255
- refresh: async () => this.refresh(),
256
- suspendAnd: async (fn) => this.suspendAnd(fn),
257
- toast: (message, tone) => this.showToast(message, tone),
258
- exit: (result) => this.exit(result ?? null)
259
- };
260
- Promise.resolve()
261
- .then(() => action.handler(context))
262
- .catch((error) => {
263
- this.showToast(error instanceof Error ? error.message : "Action failed", "error");
264
- });
265
- }
266
- async suspendAnd(fn) {
267
- this.pauseKeypress();
268
- this.driver.exitAltScreen();
269
- this.driver.enableLineWrap();
270
- this.driver.showCursor();
271
- this.driver.exitRawMode();
272
- try {
273
- return await fn();
274
- }
275
- finally {
276
- if (!this.stopped) {
277
- this.driver.enterRawMode();
278
- this.driver.enterAltScreen();
279
- this.driver.disableLineWrap();
280
- this.driver.hideCursor();
281
- this.subscribeKeypress();
282
- this.state = { ...this.state, size: normalizeSize(this.driver.getSize()) };
283
- this.render();
284
- }
285
- }
286
- }
287
- showToast(message, tone = "info") {
288
- if (this.toastTimer !== undefined) {
289
- clearTimeout(this.toastTimer);
290
- }
291
- this.state = { ...this.state, toast: { message, tone } };
292
- this.render();
293
- this.toastTimer = setTimeout(() => {
294
- this.state = { ...this.state, toast: null };
295
- this.render();
296
- }, TOAST_MS);
297
- }
298
- activePane() {
299
- return this.state.panes[this.state.activePaneIndex];
300
- }
301
- inactivePane() {
302
- return this.state.panes[this.state.activePaneIndex === 0 ? 1 : 0];
303
- }
304
- render() {
305
- if (this.stopped) {
306
- return;
307
- }
308
- const size = normalizeSize(this.driver.getSize());
309
- if (size.cols !== this.state.size.cols || size.rows !== this.state.size.rows) {
310
- this.state = { ...this.state, size };
311
- }
312
- const next = new ScreenBuffer(this.state.size.cols, this.state.size.rows);
313
- renderTwoPaneExplorer(this.state, this.config.actions, next);
314
- this.driver.write(screenToAnsi(next));
315
- }
316
- exit(result) {
317
- if (this.stopped) {
318
- return;
319
- }
320
- this.stopped = true;
321
- this.unsubscribeKeypress?.();
322
- this.unsubscribeResize?.();
323
- if (this.toastTimer !== undefined) {
324
- clearTimeout(this.toastTimer);
325
- }
326
- this.driver.destroy();
327
- this.settle?.resolve(result);
328
- }
329
- fail(error) {
330
- if (!this.stopped) {
331
- this.stopped = true;
332
- this.driver.destroy();
333
- }
334
- this.settle?.reject(error);
335
- }
336
- trace(event, fields = {}) {
337
- const trace = this.config.trace;
338
- if (trace === undefined) {
339
- return;
340
- }
341
- void Promise.resolve(trace({ event, ...fields })).catch(() => {
342
- // Diagnostic tracing must not interfere with TUI input handling.
343
- });
344
- }
345
- }
346
- export function renderTwoPaneExplorer(state, actions, screen) {
347
- screen.clear();
348
- if (state.size.cols < 60 || state.size.rows < 8) {
349
- screen.put(0, 0, fit(` ${state.title} `, state.size.cols), { bold: true });
350
- screen.put(0, 2, fit("Terminal is too small for two-pane view.", state.size.cols));
351
- screen.put(0, state.size.rows - 1, fit("q quit", state.size.cols), { dim: true });
352
- return;
353
- }
354
- const gap = 1;
355
- const paneWidth = Math.floor((state.size.cols - gap) / 2);
356
- const rightWidth = state.size.cols - paneWidth - gap;
357
- const paneHeight = Math.max(5, state.size.rows - 2);
358
- renderPane(screen, state.panes[0], {
359
- x: 0,
360
- y: 0,
361
- width: paneWidth,
362
- height: paneHeight,
363
- active: state.activePaneIndex === 0
364
- });
365
- renderPane(screen, state.panes[1], {
366
- x: paneWidth + gap,
367
- y: 0,
368
- width: rightWidth,
369
- height: paneHeight,
370
- active: state.activePaneIndex === 1
371
- });
372
- renderFooter(screen, state, actions, state.size.rows - 2);
373
- if (state.toast !== null) {
374
- screen.put(0, state.size.rows - 1, fit(` ${state.toast.message} `, state.size.cols), toneStyle(state.toast.tone));
375
- }
376
- }
377
- function renderPane(screen, pane, layout) {
378
- const borderStyle = layout.active ? { bold: true, fg: "cyan" } : { dim: true };
379
- const title = `${layout.active ? ">" : " "} ${pane.title}`;
380
- screen.put(layout.x, layout.y, `+${fit(title, layout.width - 2, "-")}+`, borderStyle);
381
- for (let y = 1; y < layout.height - 1; y += 1) {
382
- screen.put(layout.x, layout.y + y, "|", borderStyle);
383
- screen.put(layout.x + layout.width - 1, layout.y + y, "|", borderStyle);
384
- }
385
- const filterLabel = pane.filter.length > 0 ? ` /${pane.filter}` : "";
386
- screen.put(layout.x, layout.y + layout.height - 1, `+${fit(`${pane.selected.size} selected${filterLabel}`, layout.width - 2, "-")}+`, borderStyle);
387
- const visibleRows = filteredRows(pane);
388
- const bodyHeight = layout.height - 2;
389
- if (visibleRows.length === 0) {
390
- screen.put(layout.x + 2, layout.y + 2, fit(pane.emptyHint, Math.max(0, layout.width - 4)), { dim: true });
391
- return;
392
- }
393
- const start = Math.max(0, Math.min(pane.cursor - Math.floor(bodyHeight / 2), visibleRows.length - bodyHeight));
394
- for (let index = 0; index < bodyHeight; index += 1) {
395
- const row = visibleRows[start + index];
396
- if (row === undefined) {
397
- continue;
398
- }
399
- const selected = pane.selected.has(row.id);
400
- const cursor = start + index === pane.cursor;
401
- const marker = selected ? "*" : " ";
402
- const pointer = cursor && layout.active ? ">" : " ";
403
- const badge = row.badge ? ` [${row.badge.text}]` : "";
404
- const subtitle = row.subtitle ? ` ${row.subtitle}` : "";
405
- const style = cursor && layout.active ? { inverse: true } : {};
406
- screen.put(layout.x + 1, layout.y + 1 + index, fit(`${pointer}${marker} ${row.title}${badge}${subtitle}`, layout.width - 2), style);
407
- }
408
- }
409
- function renderFooter(screen, state, actions, y) {
410
- const actionHelp = actions.map((action) => `${firstKey(action.key)} ${action.label}`).join(" ");
411
- const filter = state.filterFocused ? "search: typing, enter done" : "/ search";
412
- const text = `tab pane ${filter} space select ${actionHelp} q quit`;
413
- screen.put(0, y, fit(text, state.size.cols), { dim: true });
414
- }
415
- function initialPaneState(definition) {
416
- return {
417
- id: definition.id,
418
- title: definition.title,
419
- rows: [],
420
- cursor: 0,
421
- selected: new Set(),
422
- filter: "",
423
- emptyHint: definition.emptyHint ?? "No items"
424
- };
425
- }
426
- function rowsLoaded(pane, rows) {
427
- const ids = new Set(rows.map((row) => row.id));
428
- return {
429
- ...pane,
430
- rows,
431
- selected: new Set([...pane.selected].filter((id) => ids.has(id))),
432
- cursor: Math.max(0, Math.min(pane.cursor, Math.max(0, filteredRows({ ...pane, rows }).length - 1)))
433
- };
434
- }
435
- function filteredRows(pane) {
436
- const query = pane.filter.trim().toLowerCase();
437
- if (query.length === 0) {
438
- return pane.rows;
439
- }
440
- return pane.rows.filter((row) => {
441
- const haystack = `${row.title} ${row.subtitle ?? ""} ${row.badge?.text ?? ""}`.toLowerCase();
442
- return haystack.includes(query);
443
- });
444
- }
445
- function currentRow(pane) {
446
- return filteredRows(pane)[pane.cursor];
447
- }
448
- function updateActivePane(state, update) {
449
- return state.activePaneIndex === 0
450
- ? [update(state.panes[0]), state.panes[1]]
451
- : [state.panes[0], update(state.panes[1])];
452
- }
453
- function actionMatchesKey(action, key) {
454
- const keys = Array.isArray(action.key) ? action.key : [action.key];
455
- return keys.some((candidate) => key.ch === candidate || key.name === candidate);
456
- }
457
- function isTabKey(key) {
458
- return key.name === "tab" || key.ch === "\t" || (key.name === "i" && key.ctrl);
459
- }
460
- function firstKey(key) {
461
- return Array.isArray(key) ? key[0] ?? "" : key;
462
- }
463
- function traceKey(key) {
464
- return {
465
- name: key.name,
466
- ch: key.ch,
467
- ctrl: key.ctrl,
468
- meta: key.meta,
469
- shift: key.shift
470
- };
471
- }
472
- function isQuitKey(key) {
473
- return key.ch === "q" || (key.name === "c" && key.ctrl);
474
- }
475
- function normalizeSize(size) {
476
- return {
477
- cols: Math.max(0, Math.floor(size.cols)),
478
- rows: Math.max(0, Math.floor(size.rows))
479
- };
480
- }
481
- function fit(text, width, pad = " ") {
482
- if (width <= 0) {
483
- return "";
484
- }
485
- const normalized = text.length > width ? text.slice(0, width) : text;
486
- return normalized.padEnd(width, pad);
487
- }
488
- function toneStyle(tone) {
489
- if (tone === "success") {
490
- return { fg: "green", bold: true };
491
- }
492
- if (tone === "warning") {
493
- return { fg: "yellow", bold: true };
494
- }
495
- if (tone === "error") {
496
- return { fg: "red", bold: true };
497
- }
498
- return tone === "muted" ? { dim: true } : { fg: "cyan", bold: true };
499
- }
500
- function screenToAnsi(screen) {
501
- let output = "";
502
- for (let y = 0; y < screen.height; y += 1) {
503
- output += `\u001b[${y + 1};1H`;
504
- for (let x = 0; x < screen.width; x += 1) {
505
- const cell = screen.get(x, y);
506
- output += cellToAnsi(cell);
507
- }
508
- }
509
- return output;
510
- }
@@ -1,2 +0,0 @@
1
- export { runTwoPaneExplorer } from "./explorer/index.js";
2
- export type { TwoPaneExplorerConfig } from "./explorer/index.js";
@@ -1 +0,0 @@
1
- export { runTwoPaneExplorer } from "./explorer/index.js";