@agi-fans/dsh-tui 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/hotkeys.js CHANGED
@@ -1,9 +1,8 @@
1
1
  /**
2
- * Keyboard-shortcut catalog painted by `/hotkeys`.
2
+ * Keyboard-shortcut catalog embedded in `/help`.
3
3
  * @module @agi-fans/dsh-tui
4
4
  */
5
5
  import { DEFAULT_KEYBINDINGS } from "./keybindings-config.js";
6
- import { renderMarkdown } from "./markdown.js";
7
6
  function displayKey(key) {
8
7
  return key.split('+').map((part) => {
9
8
  const normalized = part.toLowerCase();
@@ -71,6 +70,7 @@ function sections(bindings) {
71
70
  {
72
71
  title: 'Session',
73
72
  rows: [
73
+ { keys: 'Esc twice', action: 'Rewind to an earlier conversation turn' },
74
74
  { keys: 'Ctrl+C twice', action: 'Interrupt or clear, then exit' },
75
75
  { keys: 'Ctrl+Z', action: 'Suspend to the background' },
76
76
  { keys: 'Alt+L', action: 'Reset the terminal display' },
@@ -80,7 +80,7 @@ function sections(bindings) {
80
80
  { keys: '@ / ./ / ~/', action: 'Complete file paths' },
81
81
  { keys: 'Tab', action: 'Accept command or path completion' },
82
82
  { keys: '/copy', action: 'Open the copy picker' },
83
- { keys: '/hotkeys', action: 'Show this catalog' },
83
+ { keys: '/help', action: 'Show commands and keyboard shortcuts' },
84
84
  ],
85
85
  },
86
86
  ];
@@ -92,7 +92,7 @@ function tableCell(value) {
92
92
  export function hotkeyCount(bindings = DEFAULT_KEYBINDINGS) {
93
93
  return sections(bindings).reduce((total, section) => total + section.rows.length, 0);
94
94
  }
95
- /** Markdown tables used by the `/hotkeys` transcript panel. */
95
+ /** Markdown tables embedded in the `/help` transcript panel. */
96
96
  export function formatHotkeysText(bindings = DEFAULT_KEYBINDINGS) {
97
97
  return sections(bindings).flatMap((section, index) => [
98
98
  ...(index === 0 ? [] : ['']),
@@ -102,14 +102,4 @@ export function formatHotkeysText(bindings = DEFAULT_KEYBINDINGS) {
102
102
  ...section.rows.map(row => `| \`${tableCell(row.keys)}\` | ${tableCell(row.action)} |`),
103
103
  ]).join('\n');
104
104
  }
105
- /** OMP-style command heading followed by grouped native Markdown tables. */
106
- export function renderHotkeysPanel(bindings, theme, width) {
107
- const inset = width > 2 ? 1 : 0;
108
- const contentWidth = Math.max(1, width - inset * 2);
109
- const prefix = ' '.repeat(inset);
110
- const title = theme.bold(theme.fg('accent', 'Keyboard Shortcuts'))
111
- + theme.fg('muted', ` · ${hotkeyCount(bindings)} bindings`);
112
- const body = renderMarkdown(formatHotkeysText(bindings), theme, contentWidth);
113
- return [prefix + title, '', ...body.map(line => prefix + line)];
114
- }
115
105
  //# sourceMappingURL=hotkeys.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"hotkeys.js","sourceRoot":"","sources":["../src/hotkeys.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"hotkeys.js","sourceRoot":"","sources":["../src/hotkeys.ts"],"names":[],"mappings":"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"}
@@ -4,7 +4,7 @@
4
4
  * Owns the tty: raw-mode key input (editing, history, slash/tab
5
5
  * autocomplete, /settings overlay, /copy picker, Ctrl-R history search, PgUp/PgDn
6
6
  * and mouse-wheel transcript scroll, click-to-caret, Ctrl-O tool
7
- * expand, bracketed paste, double Ctrl-C exit, Ctrl-D quit),
7
+ * expand, bracketed paste, double-Escape conversation rewind, double Ctrl-C exit, Ctrl-D quit),
8
8
  * SIGWINCH reflow, and the differential renderer. In non-tty mode
9
9
  * (pipes, CI) it degrades to line-based input with plain append-only
10
10
  * printing of settled blocks.
@@ -102,7 +102,10 @@ export declare class LocalTui implements TuiService {
102
102
  /** Compatibility helper for provider-level text editing tests and embedders. */
103
103
  readline(): Promise<string | null>;
104
104
  restoreInput(submission: TuiSubmission): void;
105
+ resolveQueueEdit(submission: TuiSubmission | null): void;
105
106
  onInterrupt(listener: () => void): () => void;
107
+ onQueueEdit(listener: () => void): () => void;
108
+ onRewind(listener: () => void): () => void;
106
109
  dispose(): void;
107
110
  /** Re-render the current frame (resize reflow). */
108
111
  refresh(): void;
@@ -4,7 +4,7 @@
4
4
  * Owns the tty: raw-mode key input (editing, history, slash/tab
5
5
  * autocomplete, /settings overlay, /copy picker, Ctrl-R history search, PgUp/PgDn
6
6
  * and mouse-wheel transcript scroll, click-to-caret, Ctrl-O tool
7
- * expand, bracketed paste, double Ctrl-C exit, Ctrl-D quit),
7
+ * expand, bracketed paste, double-Escape conversation rewind, double Ctrl-C exit, Ctrl-D quit),
8
8
  * SIGWINCH reflow, and the differential renderer. In non-tty mode
9
9
  * (pipes, CI) it degrades to line-based input with plain append-only
10
10
  * printing of settled blocks.
@@ -22,12 +22,11 @@ import { buildCopyTargets, extractCopyTarget, parseCopyKind } from "./copy-targe
22
22
  import { applyHistorySearchEvent, createHistorySearch, hitTestHistorySearch, } from "./history-search.js";
23
23
  import { InputEditor, lineEnd, lineStart } from "./editor.js";
24
24
  import { applySettingsEvent, createSettings, hitTestSettings, selectSetting, tuiSettingItems, } from "./settings-list.js";
25
- import { applyEvent, blockLines, initialTranscript, renderView, TRANSCRIPT_FAST_SCROLL, TRANSCRIPT_WHEEL_SCROLL, } from "./event-views.js";
25
+ import { applyEvent, blockLines, initialTranscript, replayEvents, renderView, TRANSCRIPT_FAST_SCROLL, TRANSCRIPT_WHEEL_SCROLL, } from "./event-views.js";
26
26
  import { flushPending, MOUSE_TRACKING_OFF, MOUSE_TRACKING_ON, parseKeys } from "./keys.js";
27
27
  import { LineRenderer } from "./renderer.js";
28
28
  import { hitTestEditor } from "./box.js";
29
29
  import { createTheme, detectTrueColor, parseThemeName } from "./theme.js";
30
- import { formatWorkspaceText } from "./pwd.js";
31
30
  import { TUI_SETTINGS_NAMESPACE, TuiSettingsSchema } from "./tui-settings.js";
32
31
  import { defaultStatusBarConfig, resolveStatusBarConfig } from "./status-config.js";
33
32
  import { HistoryStore } from "./history-store.js";
@@ -37,10 +36,12 @@ import { settingsNamespace } from '@deepseek-ai/dsh-settings';
37
36
  import { movePromptSelection, filteredPromptOptions, selectedPromptAnswer, selectedFilteredPromptAnswer, togglePromptSelection, } from "./prompt-selector.js";
38
37
  import { resolveProjectContext } from "./project-context.js";
39
38
  import { pickWelcomeTips } from "./welcome-tips.js";
39
+ import { formatHotkeysText, hotkeyCount } from "./hotkeys.js";
40
40
  import { imageMarker, imagePathCandidates, probeImageDimensions, readImageFile, readImageFromClipboard, readMacClipboardFiles, } from "./image-paste.js";
41
41
  const APP_NAME = 'omdsh';
42
42
  const APP_VERSION = '0.1.0';
43
43
  const DOUBLE_CTRL_C_MS = 500;
44
+ const DOUBLE_ESCAPE_MS = 500;
44
45
  function shortenPath(cwd) {
45
46
  const home = homedir();
46
47
  if (cwd === home)
@@ -73,11 +74,17 @@ export class LocalTui {
73
74
  #settings = null;
74
75
  #copySelector = null;
75
76
  #pending = null;
76
- #queue = [];
77
+ #queuedSubmissions = [];
78
+ /** Newer queue entries temporarily detached while Up browses backward. */
79
+ #queueEditNewer = null;
80
+ #queueEditPending = false;
77
81
  #quitRequested = false;
78
82
  #resumeHintRequested = false;
79
83
  #lastSigintTime = 0;
84
+ #lastEscapeTime = 0;
80
85
  #interrupts = new Set();
86
+ #queueEdits = new Set();
87
+ #rewinds = new Set();
81
88
  #disposed = false;
82
89
  #pendingKeys = '';
83
90
  #escapeTimer = null;
@@ -202,6 +209,8 @@ export class LocalTui {
202
209
  }
203
210
  }
204
211
  setStatus(status) {
212
+ if (this.#state.status === 'compacting')
213
+ return;
205
214
  this.#state = { ...this.#state, status };
206
215
  this.#syncTick();
207
216
  if (this.#tty)
@@ -282,10 +291,8 @@ export class LocalTui {
282
291
  });
283
292
  }
284
293
  replaceSession(events, presentations) {
285
- let state = initialTranscript();
286
- for (const event of events)
287
- state = applyEvent(state, event, presentations?.get(event.seq));
288
- this.#state = { ...state, status: 'idle' };
294
+ const state = replayEvents(events, presentations);
295
+ this.#state = { ...state, status: 'idle', compactCommandId: undefined };
289
296
  this.#plainPrinted = 0;
290
297
  this.#followTail();
291
298
  if (this.#tty)
@@ -337,9 +344,11 @@ export class LocalTui {
337
344
  return this.#readlinePlain();
338
345
  // Lines submitted while a turn was still running were queued instead of
339
346
  // dropped; serve the oldest before waiting for fresh input.
340
- const queued = this.#queue.shift();
341
- if (queued !== undefined)
347
+ const queued = this.#queuedSubmissions.shift();
348
+ if (queued !== undefined) {
349
+ this.#render();
342
350
  return Promise.resolve(queued);
351
+ }
343
352
  return new Promise((resolve) => {
344
353
  this.#pending = { resolve };
345
354
  });
@@ -363,10 +372,49 @@ export class LocalTui {
363
372
  if (this.#tty)
364
373
  this.#render();
365
374
  }
375
+ resolveQueueEdit(submission) {
376
+ if (!this.#queueEditPending)
377
+ return;
378
+ this.#queueEditPending = false;
379
+ if (submission === null) {
380
+ if (this.#editor.text === '' && this.#images.length === 0 && this.#queueEditNewer?.length === 0) {
381
+ this.#queueEditNewer = null;
382
+ }
383
+ return;
384
+ }
385
+ if (this.#queueEditNewer === null)
386
+ this.#queueEditNewer = [];
387
+ if (this.#editor.text !== '' || this.#images.length > 0) {
388
+ this.#queueEditNewer.unshift(this.#currentSubmission());
389
+ }
390
+ this.#replaceInput(submission);
391
+ }
392
+ #currentSubmission() {
393
+ return {
394
+ text: this.#editor.text,
395
+ images: this.#images.map(image => ({ ...image })),
396
+ };
397
+ }
398
+ #replaceInput(submission) {
399
+ this.#images = submission.images.map(image => ({ ...image }));
400
+ this.#editor.setText(submission.text);
401
+ this.#historyIndex = 0;
402
+ this.#refreshAutocomplete();
403
+ if (this.#tty)
404
+ this.#render();
405
+ }
366
406
  onInterrupt(listener) {
367
407
  this.#interrupts.add(listener);
368
408
  return () => { this.#interrupts.delete(listener); };
369
409
  }
410
+ onQueueEdit(listener) {
411
+ this.#queueEdits.add(listener);
412
+ return () => { this.#queueEdits.delete(listener); };
413
+ }
414
+ onRewind(listener) {
415
+ this.#rewinds.add(listener);
416
+ return () => { this.#rewinds.delete(listener); };
417
+ }
370
418
  dispose() {
371
419
  if (this.#disposed)
372
420
  return;
@@ -471,7 +519,7 @@ export class LocalTui {
471
519
  this.#term.output.write(out);
472
520
  }
473
521
  #busy() {
474
- if (this.#state.status === 'running')
522
+ if (this.#state.status !== 'idle')
475
523
  return true;
476
524
  return this.#state.blocks.some((block) => block.kind === 'tool' && block.status === 'running');
477
525
  }
@@ -506,6 +554,9 @@ export class LocalTui {
506
554
  input: this.#editor.text,
507
555
  inputCursor: this.#editor.cursor,
508
556
  inputImages: this.#images.length,
557
+ queuedSubmissions: this.#queueEditNewer === null
558
+ ? this.#queuedSubmissions
559
+ : [...this.#queuedSubmissions, ...this.#queueEditNewer],
509
560
  colors: this.#colors,
510
561
  pwd: this.#pwd,
511
562
  ...(this.#branch !== undefined ? { branch: this.#branch } : {}),
@@ -761,6 +812,22 @@ export class LocalTui {
761
812
  this.#editor.setText(text, Math.min(this.#editor.cursor, text.length));
762
813
  }
763
814
  #dispatch(event) {
815
+ if (this.#state.status === 'compacting') {
816
+ if (event.type === 'mouse' && event.wheel !== null) {
817
+ this.#scrollBy(event.wheel < 0 ? -TRANSCRIPT_WHEEL_SCROLL : TRANSCRIPT_WHEEL_SCROLL, true);
818
+ return;
819
+ }
820
+ if (event.type === 'key' && event.id === 'pageUp') {
821
+ this.#scrollBy(-this.#pageSize());
822
+ return;
823
+ }
824
+ if (event.type === 'key' && event.id === 'pageDown') {
825
+ this.#scrollBy(this.#pageSize());
826
+ return;
827
+ }
828
+ if (event.type !== 'key' || event.id !== 'ctrl+c')
829
+ return;
830
+ }
764
831
  // Clipboard image inspection is asynchronous. Preserve the exact key
765
832
  // order so a fast Ctrl+V, Enter submits the finished image draft rather
766
833
  // than an empty prompt.
@@ -789,9 +856,12 @@ export class LocalTui {
789
856
  return;
790
857
  }
791
858
  if (event.type === 'mouse') {
859
+ this.#lastEscapeTime = 0;
792
860
  this.#handleMouse(event);
793
861
  return;
794
862
  }
863
+ if (event.type !== 'key' || event.id !== 'escape')
864
+ this.#lastEscapeTime = 0;
795
865
  if (this.#handlePrompt(event))
796
866
  return;
797
867
  if (event.type === 'key') {
@@ -830,7 +900,7 @@ export class LocalTui {
830
900
  return;
831
901
  }
832
902
  this.#lastSigintTime = now;
833
- if (this.#state.status === 'running') {
903
+ if (this.#state.status !== 'idle') {
834
904
  for (const listener of this.#interrupts)
835
905
  listener();
836
906
  }
@@ -863,8 +933,29 @@ export class LocalTui {
863
933
  this.#applySearch(event);
864
934
  return;
865
935
  }
866
- if (this.#handleAutocomplete(event))
936
+ if (this.#handleAutocomplete(event)) {
937
+ if (event.type === 'key' && event.id === 'escape')
938
+ this.#lastEscapeTime = 0;
867
939
  return;
940
+ }
941
+ if (event.type === 'key' && event.id === 'escape') {
942
+ if (this.#state.status !== 'idle' || this.#pending === null
943
+ || this.#editor.text !== '' || this.#images.length > 0) {
944
+ this.#lastEscapeTime = 0;
945
+ }
946
+ else {
947
+ const now = Date.now();
948
+ if (now - this.#lastEscapeTime < DOUBLE_ESCAPE_MS) {
949
+ this.#lastEscapeTime = 0;
950
+ for (const listener of this.#rewinds)
951
+ listener();
952
+ }
953
+ else {
954
+ this.#lastEscapeTime = now;
955
+ }
956
+ return;
957
+ }
958
+ }
868
959
  if (event.type === 'key' && (event.id === 'backspace' || event.id === 'delete')
869
960
  && this.#removeImageAtCursor(event.id))
870
961
  return;
@@ -1428,6 +1519,8 @@ export class LocalTui {
1428
1519
  return;
1429
1520
  }
1430
1521
  if (command.kind === 'historyPrev') {
1522
+ if (this.#restoreLatestQueuedSubmission())
1523
+ return;
1431
1524
  this.#historyPrev();
1432
1525
  return;
1433
1526
  }
@@ -1445,6 +1538,8 @@ export class LocalTui {
1445
1538
  if (command.kind === 'clear') {
1446
1539
  this.#editor.clear();
1447
1540
  this.#images = [];
1541
+ this.#queueEditNewer = null;
1542
+ this.#queueEditPending = false;
1448
1543
  this.#historyIndex = 0;
1449
1544
  this.#ac = null;
1450
1545
  this.#search = null;
@@ -1482,6 +1577,41 @@ export class LocalTui {
1482
1577
  this.#refreshAutocomplete();
1483
1578
  this.#render();
1484
1579
  }
1580
+ #restoreLatestQueuedSubmission() {
1581
+ if (this.#queueEditPending)
1582
+ return true;
1583
+ if (this.#queueEditNewer !== null) {
1584
+ const previous = this.#queuedSubmissions.pop();
1585
+ if (previous !== undefined) {
1586
+ this.#queueEditNewer.unshift(this.#currentSubmission());
1587
+ this.#replaceInput(previous);
1588
+ return true;
1589
+ }
1590
+ const hasDurableFollowup = this.#state.nextTurnInbox.some(message => message.source.kind === 'user');
1591
+ if (!hasDurableFollowup || this.#queueEdits.size === 0)
1592
+ return true;
1593
+ this.#queueEditPending = true;
1594
+ for (const listener of this.#queueEdits)
1595
+ listener();
1596
+ return true;
1597
+ }
1598
+ if (this.#editor.text !== '' || this.#images.length > 0 || this.#historyIndex !== 0)
1599
+ return false;
1600
+ const submission = this.#queuedSubmissions.pop();
1601
+ if (submission !== undefined) {
1602
+ this.#queueEditNewer = [];
1603
+ this.#replaceInput(submission);
1604
+ return true;
1605
+ }
1606
+ const hasDurableFollowup = this.#state.nextTurnInbox.some(message => message.source.kind === 'user');
1607
+ if (!hasDurableFollowup || this.#queueEdits.size === 0)
1608
+ return false;
1609
+ this.#queueEditNewer = [];
1610
+ this.#queueEditPending = true;
1611
+ for (const listener of this.#queueEdits)
1612
+ listener();
1613
+ return true;
1614
+ }
1485
1615
  #historyNext() {
1486
1616
  if (this.#images.length > 0)
1487
1617
  return;
@@ -1512,6 +1642,7 @@ export class LocalTui {
1512
1642
  }
1513
1643
  const images = this.#images.map(image => ({ ...image }));
1514
1644
  const submittedText = images.length > 0 ? text.trim() : text;
1645
+ const queueEditNewer = this.#queueEditNewer;
1515
1646
  const historyText = images.reduce((value, image, index) => value.replaceAll(imageMarker(index, image), ''), submittedText)
1516
1647
  .replace(/[ \t]{2,}/gu, ' ')
1517
1648
  .trim();
@@ -1521,6 +1652,8 @@ export class LocalTui {
1521
1652
  }
1522
1653
  this.#historyIndex = 0;
1523
1654
  this.#draft = '';
1655
+ this.#queueEditNewer = null;
1656
+ this.#queueEditPending = false;
1524
1657
  this.#editor.setText('');
1525
1658
  this.#images = [];
1526
1659
  this.#ac = null;
@@ -1530,18 +1663,26 @@ export class LocalTui {
1530
1663
  this.#followTail();
1531
1664
  const slash = images.length === 0 ? parseSlashInput(submittedText) : null;
1532
1665
  if (slash !== null) {
1666
+ if (queueEditNewer !== null)
1667
+ this.#queuedSubmissions.push(...queueEditNewer);
1533
1668
  this.#runSlash(slash.name, slash.args);
1534
1669
  return;
1535
1670
  }
1536
- this.#render();
1537
1671
  const pending = this.#pending;
1538
1672
  if (pending !== null) {
1539
1673
  this.#pending = null;
1540
1674
  pending.resolve({ text: submittedText, images });
1675
+ if (queueEditNewer !== null)
1676
+ this.#queuedSubmissions.push(...queueEditNewer);
1541
1677
  }
1542
- else if (submittedText !== '' || images.length > 0) {
1543
- this.#queue.push({ text: submittedText, images });
1678
+ else if (submittedText !== '' || images.length > 0 || queueEditNewer !== null) {
1679
+ if (submittedText !== '' || images.length > 0) {
1680
+ this.#queuedSubmissions.push({ text: submittedText, images });
1681
+ }
1682
+ if (queueEditNewer !== null)
1683
+ this.#queuedSubmissions.push(...queueEditNewer);
1544
1684
  }
1685
+ this.#render();
1545
1686
  }
1546
1687
  #runSlash(name, args = '') {
1547
1688
  if (name === '') {
@@ -1569,11 +1710,6 @@ export class LocalTui {
1569
1710
  this.#runSettings(args);
1570
1711
  return;
1571
1712
  }
1572
- if (command.name === 'hotkeys') {
1573
- this.#hotkeyCatalog();
1574
- this.#render();
1575
- return;
1576
- }
1577
1713
  if (command.name === 'copy') {
1578
1714
  void this.#runCopy(args);
1579
1715
  return;
@@ -1583,17 +1719,7 @@ export class LocalTui {
1583
1719
  this.#render();
1584
1720
  return;
1585
1721
  }
1586
- if (command.name === 'pwd') {
1587
- this.#notice(formatWorkspaceText({
1588
- cwd: process.cwd(),
1589
- model: this.#model,
1590
- ...(this.#branch !== undefined ? { branch: this.#branch } : {}),
1591
- }));
1592
- this.#render();
1593
- return;
1594
- }
1595
1722
  if (!BUILTIN_SLASH_COMMANDS.some((entry) => entry.name === command.name)) {
1596
- this.#render();
1597
1723
  const raw = '/' + name + (args === '' ? '' : ' ' + args);
1598
1724
  const pending = this.#pending;
1599
1725
  if (pending !== null) {
@@ -1601,13 +1727,24 @@ export class LocalTui {
1601
1727
  pending.resolve({ text: raw, images: [] });
1602
1728
  }
1603
1729
  else {
1604
- this.#queue.push({ text: raw, images: [] });
1730
+ this.#queuedSubmissions.push({ text: raw, images: [] });
1605
1731
  }
1732
+ this.#render();
1606
1733
  return;
1607
1734
  }
1608
1735
  this.#state = {
1609
1736
  ...this.#state,
1610
- blocks: [...this.#state.blocks, { kind: 'commandOutput', command: 'help', text: formatHelpText(this.#commands()) }],
1737
+ blocks: [...this.#state.blocks, {
1738
+ kind: 'commandOutput',
1739
+ command: 'help',
1740
+ text: [
1741
+ formatHelpText(this.#commands()),
1742
+ '',
1743
+ `**Keyboard Shortcuts · ${hotkeyCount(this.#keybindings)} bindings**`,
1744
+ '',
1745
+ formatHotkeysText(this.#keybindings),
1746
+ ].join('\n'),
1747
+ }],
1611
1748
  };
1612
1749
  this.#focusLatestBlock();
1613
1750
  this.#render();
@@ -1684,13 +1821,6 @@ export class LocalTui {
1684
1821
  };
1685
1822
  this.#focusLatestBlock();
1686
1823
  }
1687
- #hotkeyCatalog() {
1688
- this.#state = {
1689
- ...this.#state,
1690
- blocks: [...this.#state.blocks, { kind: 'hotkeyCatalog', bindings: this.#keybindings }],
1691
- };
1692
- this.#focusLatestBlock();
1693
- }
1694
1824
  #focusLatestBlock() {
1695
1825
  this.#follow = false;
1696
1826
  this.#focusBlock = Math.max(0, this.#state.blocks.length - 1);