@agi-fans/dsh-tui 0.25.0 → 0.26.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/CHANGELOG.md +24 -2
- package/lib/chrome/box.d.ts +7 -0
- package/lib/chrome/box.js +1 -0
- package/lib/chrome/box.js.map +1 -1
- package/lib/chrome/main-screen-renderer.d.ts +2 -0
- package/lib/chrome/main-screen-renderer.js +6 -0
- package/lib/chrome/main-screen-renderer.js.map +1 -1
- package/lib/chrome/renderer.d.ts +12 -0
- package/lib/chrome/renderer.js.map +1 -1
- package/lib/chrome/status-line.js +3 -2
- package/lib/chrome/status-line.js.map +1 -1
- package/lib/chrome/width.d.ts +2 -0
- package/lib/chrome/width.js +10 -0
- package/lib/chrome/width.js.map +1 -1
- package/lib/commands/session.js +23 -12
- package/lib/commands/session.js.map +1 -1
- package/lib/definition.d.ts +11 -2
- package/lib/input/actions.d.ts +1 -1
- package/lib/input/actions.js +16 -5
- package/lib/input/actions.js.map +1 -1
- package/lib/input/editor.js +4 -0
- package/lib/input/editor.js.map +1 -1
- package/lib/input/key-platform.d.ts +2 -0
- package/lib/input/key-platform.js +8 -0
- package/lib/input/key-platform.js.map +1 -0
- package/lib/input/keybindings-config.d.ts +2 -1
- package/lib/input/keybindings-config.js +14 -9
- package/lib/input/keybindings-config.js.map +1 -1
- package/lib/input/keys.d.ts +2 -0
- package/lib/input/keys.js +10 -0
- package/lib/input/keys.js.map +1 -1
- package/lib/locale/zh.d.ts +3 -2
- package/lib/locale/zh.js +3 -2
- package/lib/locale/zh.js.map +1 -1
- package/lib/runtime/help-reader.d.ts +1 -1
- package/lib/runtime/help-reader.js +2 -2
- package/lib/runtime/help-reader.js.map +1 -1
- package/lib/runtime/provider-local.d.ts +4 -1
- package/lib/runtime/provider-local.js +209 -30
- package/lib/runtime/provider-local.js.map +1 -1
- package/lib/session/session-controller.js +40 -6
- package/lib/session/session-controller.js.map +1 -1
- package/lib/views/file-browser.d.ts +3 -0
- package/lib/views/file-browser.js +1 -0
- package/lib/views/file-browser.js.map +1 -1
- package/lib/views/hotkeys.d.ts +4 -4
- package/lib/views/hotkeys.js +24 -14
- package/lib/views/hotkeys.js.map +1 -1
- package/lib/views/settings-list.js +2 -2
- package/lib/views/settings-list.js.map +1 -1
- package/lib/views/transcript-render.d.ts +1 -0
- package/lib/views/transcript-render.js +25 -12
- package/lib/views/transcript-render.js.map +1 -1
- package/package.json +1 -1
|
@@ -64,11 +64,11 @@ import { appendTrajectoryEvent, applyTrajectoryEvent, createTrajectory, trajecto
|
|
|
64
64
|
import { applyEvent, applyStreamChunk, renderedBlocks, formatAgentElapsed, rosterPreviewStart, initialTranscript, replayEvents, renderView, turnGroups, transcriptSeam, TRANSCRIPT_FAST_SCROLL, withWorkspaceSummary, } from "../views/event-views.js";
|
|
65
65
|
import { applyTranscriptSearchEvent, createTranscriptSearch, searchMatches, } from "../views/transcript-search.js";
|
|
66
66
|
import { nextRevealStep, revealStreamingAssistant, streamingAssistantKey, streamingAssistantUnits, } from "../views/streaming-reveal.js";
|
|
67
|
-
import { flushPending, parseKeys } from "../input/keys.js";
|
|
67
|
+
import { flushPending, normalizeKeyId, parseKeys } from "../input/keys.js";
|
|
68
68
|
import { sanitizeDisplayLine } from "../chrome/renderer.js";
|
|
69
69
|
import { blockFileAction } from "../views/file-actions.js";
|
|
70
70
|
import { fileDocument, reviewFiles } from "./file-review.js";
|
|
71
|
-
import { stripAnsi } from "../chrome/width.js";
|
|
71
|
+
import { indexAtCell, stripAnsi } from "../chrome/width.js";
|
|
72
72
|
import { MainScreenRenderer } from "../chrome/main-screen-renderer.js";
|
|
73
73
|
import { supportsHyperlinks } from "./terminal-probe.js";
|
|
74
74
|
import { colorDisabledByEnv, createTheme, detectColorDepth } from "../chrome/theme.js";
|
|
@@ -207,6 +207,8 @@ export class LocalTui {
|
|
|
207
207
|
#keyTest;
|
|
208
208
|
#mouseActionPress;
|
|
209
209
|
#fileActionFrame;
|
|
210
|
+
#inputFrame;
|
|
211
|
+
#inputPress;
|
|
210
212
|
#imageTemporary = new Set();
|
|
211
213
|
#terminalProfile;
|
|
212
214
|
#resizeDebounceMs;
|
|
@@ -227,7 +229,7 @@ export class LocalTui {
|
|
|
227
229
|
#files = new ComposerFiles({ text: () => this.#editor.text,
|
|
228
230
|
append: marker => { this.#editor.setCursor(this.#editor.text.length); this.#editor.handle({ type: 'text', value: (this.#editor.text === '' ? '' : ' ') + marker }); } });
|
|
229
231
|
insertText(text) {
|
|
230
|
-
if (text === '')
|
|
232
|
+
if (text === '' || this.#inspected?.writable === false)
|
|
231
233
|
return;
|
|
232
234
|
const before = this.#editor.text.slice(0, this.#editor.cursor);
|
|
233
235
|
const after = this.#editor.text.slice(this.#editor.cursor);
|
|
@@ -289,6 +291,7 @@ export class LocalTui {
|
|
|
289
291
|
#resumeHintRequested = false;
|
|
290
292
|
#lastSigintTime = 0;
|
|
291
293
|
#lastEscapeTime = 0;
|
|
294
|
+
#receivedModifiedEnter = new Set();
|
|
292
295
|
#inspectEscapeFenceUntil = 0;
|
|
293
296
|
#interrupts = new Set();
|
|
294
297
|
#queueEdits = new Set();
|
|
@@ -436,6 +439,8 @@ export class LocalTui {
|
|
|
436
439
|
#expandedReasoning = new Set();
|
|
437
440
|
#expandedToolRows = new Set();
|
|
438
441
|
#processSelection;
|
|
442
|
+
#promptAnchor;
|
|
443
|
+
#transcriptScopeChanged = false;
|
|
439
444
|
#closedReasoning = new Set();
|
|
440
445
|
/**
|
|
441
446
|
* The last action's confirmation, drawn on the composer's bottom border.
|
|
@@ -505,6 +510,7 @@ export class LocalTui {
|
|
|
505
510
|
#keybindingsPath;
|
|
506
511
|
#keybindings;
|
|
507
512
|
#cwd;
|
|
513
|
+
#rootDirectory;
|
|
508
514
|
#projectRoot;
|
|
509
515
|
#home;
|
|
510
516
|
#listDir;
|
|
@@ -559,6 +565,7 @@ export class LocalTui {
|
|
|
559
565
|
this.#cwd = paths.cwd ?? fallback.cwd;
|
|
560
566
|
this.#readImagePath = paths.readImagePath ?? (path => readImageFile(path, this.#cwd));
|
|
561
567
|
const project = resolveProjectContext(this.#cwd);
|
|
568
|
+
this.#rootDirectory = { cwd: this.#cwd, branch: project.gitLabel };
|
|
562
569
|
this.#projectRoot = paths.projectRoot ?? project.root;
|
|
563
570
|
this.#home = paths.home ?? fallback.home;
|
|
564
571
|
this.#listDir = paths.listDir ?? fallback.listDir;
|
|
@@ -606,6 +613,7 @@ export class LocalTui {
|
|
|
606
613
|
// live window so it stays anchored at the bottom.
|
|
607
614
|
this.#selection.clear();
|
|
608
615
|
this.#mouseActionPress = undefined;
|
|
616
|
+
this.#inputPress = undefined;
|
|
609
617
|
this.#workspacePress = undefined;
|
|
610
618
|
this.#jumpPress = undefined;
|
|
611
619
|
this.#disclosurePress = undefined;
|
|
@@ -649,23 +657,38 @@ export class LocalTui {
|
|
|
649
657
|
this.#syncWindowTitle();
|
|
650
658
|
}
|
|
651
659
|
/** Follow the viewed Session without replacing its transcript or native history. */
|
|
652
|
-
setWorkingDirectory(cwd) {
|
|
660
|
+
setWorkingDirectory(cwd, options = {}) {
|
|
661
|
+
const root = options.root ?? this.#inspected === undefined;
|
|
662
|
+
const project = resolveProjectContext(cwd);
|
|
663
|
+
if (root) {
|
|
664
|
+
this.#rootDirectory = { cwd, branch: project.gitLabel };
|
|
665
|
+
this.#syncWindowTitle();
|
|
666
|
+
if (this.#inspected !== undefined)
|
|
667
|
+
return;
|
|
668
|
+
}
|
|
653
669
|
if (cwd === this.#cwd && cwd === this.#projectRoot)
|
|
654
670
|
return;
|
|
655
|
-
this.#cwd
|
|
671
|
+
this.#setViewedDirectory(cwd);
|
|
672
|
+
this.#syncWindowTitle();
|
|
673
|
+
if (this.#tty)
|
|
674
|
+
this.#render();
|
|
675
|
+
}
|
|
676
|
+
#setViewedDirectory(cwd) {
|
|
656
677
|
const project = resolveProjectContext(cwd);
|
|
678
|
+
this.#cwd = cwd;
|
|
657
679
|
this.#projectRoot = cwd;
|
|
658
680
|
this.#pwd = shortenPath(cwd);
|
|
659
681
|
this.#branch = project.gitLabel;
|
|
660
682
|
this.#refreshAutocomplete();
|
|
661
|
-
this.#syncWindowTitle();
|
|
662
|
-
if (this.#tty)
|
|
663
|
-
this.#render();
|
|
664
683
|
}
|
|
665
684
|
/** Refresh Git metadata without changing the Session's effective directory. */
|
|
666
685
|
#refreshProjectContext() {
|
|
667
686
|
const branch = refreshProjectContext(this.#cwd).gitLabel;
|
|
668
|
-
|
|
687
|
+
const rootBranch = this.#rootDirectory.cwd === this.#cwd ? branch : refreshProjectContext(this.#rootDirectory.cwd).gitLabel;
|
|
688
|
+
const rootChanged = rootBranch !== this.#rootDirectory.branch;
|
|
689
|
+
if (rootChanged)
|
|
690
|
+
this.#rootDirectory = { ...this.#rootDirectory, branch: rootBranch };
|
|
691
|
+
if (branch === this.#branch && !rootChanged)
|
|
669
692
|
return;
|
|
670
693
|
this.#branch = branch;
|
|
671
694
|
this.#syncWindowTitle();
|
|
@@ -842,6 +865,7 @@ export class LocalTui {
|
|
|
842
865
|
const current = this.#subagents?.agents.find(agent => agent.id === inspected.id);
|
|
843
866
|
if (current !== undefined) {
|
|
844
867
|
this.#inspected = {
|
|
868
|
+
...inspected,
|
|
845
869
|
id: current.id,
|
|
846
870
|
label: current.label,
|
|
847
871
|
phase: current.phase,
|
|
@@ -860,15 +884,25 @@ export class LocalTui {
|
|
|
860
884
|
setInspectedSubagent(inspected) {
|
|
861
885
|
if (this.#inspected?.id === inspected?.id && this.#inspected?.label === inspected?.label
|
|
862
886
|
&& this.#inspected?.phase === inspected?.phase && this.#inspected?.needsApproval === inspected?.needsApproval && this.#inspected?.mode === inspected?.mode
|
|
863
|
-
&& this.#inspected?.writable === inspected?.writable
|
|
887
|
+
&& this.#inspected?.writable === inspected?.writable && this.#inspected?.model === inspected?.model
|
|
888
|
+
&& this.#inspected?.reasoningEffort === inspected?.reasoningEffort && this.#inspected?.stats === inspected?.stats
|
|
889
|
+
&& this.#inspected?.controls === inspected?.controls)
|
|
864
890
|
return;
|
|
865
891
|
if (this.#inspected?.id !== inspected?.id) {
|
|
892
|
+
this.#transcriptScopeChanged = true;
|
|
866
893
|
this.#workspacePress = undefined;
|
|
867
894
|
this.#fileReviewAbort?.abort();
|
|
868
895
|
this.#closeFileBrowser();
|
|
896
|
+
this.#transcriptSearch = null;
|
|
897
|
+
this.#followTail();
|
|
869
898
|
}
|
|
870
|
-
this.#
|
|
899
|
+
const leaving = this.#inspected !== undefined && inspected === undefined;
|
|
871
900
|
this.#inspected = inspected === undefined ? undefined : { ...inspected };
|
|
901
|
+
if (leaving) {
|
|
902
|
+
this.#setViewedDirectory(this.#rootDirectory.cwd);
|
|
903
|
+
this.#syncWindowTitle();
|
|
904
|
+
}
|
|
905
|
+
this.#switchDraft(inspected?.id ?? this.#sessionId);
|
|
872
906
|
this.#drainPendingInput();
|
|
873
907
|
if (inspected !== undefined) {
|
|
874
908
|
this.#inspectEscapeFenceUntil = 0;
|
|
@@ -902,7 +936,7 @@ export class LocalTui {
|
|
|
902
936
|
this.#settings = null;
|
|
903
937
|
this.#search = null;
|
|
904
938
|
this.#ac = null;
|
|
905
|
-
const browser = new FileBrowser(request, () => { if (!this.#disposed)
|
|
939
|
+
const browser = new FileBrowser({ ...request, canQuote: request.canQuote && (this.#inspected === undefined || this.#inspected.writable) }, () => { if (!this.#disposed)
|
|
906
940
|
this.#render(); }, () => {
|
|
907
941
|
if (this.#fileBrowser === browser)
|
|
908
942
|
this.#fileBrowser = null;
|
|
@@ -1370,10 +1404,23 @@ export class LocalTui {
|
|
|
1370
1404
|
// A notice does not turn a blank session into a document that /new needs
|
|
1371
1405
|
// to separate. Refreshes are the same document.
|
|
1372
1406
|
const boundary = reason === 'open' || (reason === 'new' && this.#state.blocks.some(block => block.kind !== 'notice'));
|
|
1373
|
-
const
|
|
1407
|
+
const refreshing = reason === 'refresh' && !this.#transcriptScopeChanged;
|
|
1408
|
+
this.#transcriptScopeChanged = false;
|
|
1409
|
+
const seam = refreshing && this.#state.blocks[0]?.kind === 'notice' && this.#state.blocks[0].boundary === true
|
|
1410
|
+
? this.#state.blocks[0] : undefined;
|
|
1411
|
+
const blocks = boundary ? [transcriptSeam('session-opened'), ...state.blocks] : seam === undefined ? state.blocks : [seam, ...state.blocks];
|
|
1374
1412
|
this.#state = { ...state, status, compactCommandId: undefined, blocks };
|
|
1375
1413
|
this.#plain.resetPrinted();
|
|
1376
|
-
this.#
|
|
1414
|
+
this.#promptAnchor = undefined;
|
|
1415
|
+
if (!refreshing) {
|
|
1416
|
+
this.#transcriptSearch = null;
|
|
1417
|
+
this.#followTail();
|
|
1418
|
+
}
|
|
1419
|
+
else if (this.#transcriptSearch !== null) {
|
|
1420
|
+
const match = this.#searchOccurrences()[this.#transcriptSearch.focus];
|
|
1421
|
+
if (match !== undefined)
|
|
1422
|
+
this.#focusSearchMatch(match);
|
|
1423
|
+
}
|
|
1377
1424
|
this.#deferInitialRender = false;
|
|
1378
1425
|
this.#renderer.startEpoch({ replay: reason === 'refresh' ? 'viewport' : status === 'idle' ? 'full' : 'pinned' });
|
|
1379
1426
|
if (this.#tty)
|
|
@@ -1383,6 +1430,7 @@ export class LocalTui {
|
|
|
1383
1430
|
}
|
|
1384
1431
|
setSession(info) {
|
|
1385
1432
|
if (info.id !== this.#sessionId) {
|
|
1433
|
+
this.#transcriptScopeChanged = true;
|
|
1386
1434
|
this.#workspacePress = undefined;
|
|
1387
1435
|
this.#fileReviewAbort?.abort();
|
|
1388
1436
|
this.#closeFileBrowser();
|
|
@@ -1420,7 +1468,7 @@ export class LocalTui {
|
|
|
1420
1468
|
const status = this.#herdr.programStatus;
|
|
1421
1469
|
this.#writeWorkingDirectory(this.#cwd);
|
|
1422
1470
|
this.#syncProgress(status.state);
|
|
1423
|
-
const title = terminalTitle(this.#terminalTitle, status, this.#sessionTitle, this.#cwd, this.#branch);
|
|
1471
|
+
const title = terminalTitle(this.#terminalTitle, status, this.#sessionTitle, this.#rootDirectory.cwd, this.#rootDirectory.branch);
|
|
1424
1472
|
if (title !== this.#writtenWindowTitle && !(this.#terminalTitle === 'off' && this.#writtenWindowTitle === undefined)) {
|
|
1425
1473
|
this.#writtenWindowTitle = title;
|
|
1426
1474
|
this.#term.output.write(`\x1b]2;${title}\x07`);
|
|
@@ -2005,6 +2053,19 @@ export class LocalTui {
|
|
|
2005
2053
|
refresh() {
|
|
2006
2054
|
this.#render();
|
|
2007
2055
|
}
|
|
2056
|
+
searchTranscript(query) {
|
|
2057
|
+
if (!this.#tty || this.#disposed || query.trim() === '')
|
|
2058
|
+
return;
|
|
2059
|
+
// Commit the resumed document before moving its live viewport into history.
|
|
2060
|
+
this.#render();
|
|
2061
|
+
this.#transcriptSearch = { query, editing: false, focus: 0 };
|
|
2062
|
+
this.#promptAnchor = undefined;
|
|
2063
|
+
this.#ac = null;
|
|
2064
|
+
const match = this.#searchOccurrences()[0];
|
|
2065
|
+
if (match !== undefined)
|
|
2066
|
+
this.#focusSearchMatch(match);
|
|
2067
|
+
this.#render();
|
|
2068
|
+
}
|
|
2008
2069
|
#busy() {
|
|
2009
2070
|
if (this.#state.status !== 'idle')
|
|
2010
2071
|
return true;
|
|
@@ -2230,6 +2291,13 @@ export class LocalTui {
|
|
|
2230
2291
|
if (this.#probeGate)
|
|
2231
2292
|
return;
|
|
2232
2293
|
const width = this.#term.width();
|
|
2294
|
+
const alternateSendKey = keysForAction(Object.fromEntries(Object.entries(this.#keybindings).filter(([key]) => {
|
|
2295
|
+
if (key === 'super+enter')
|
|
2296
|
+
return this.#receivedModifiedEnter.has(key);
|
|
2297
|
+
if (key === 'ctrl+enter')
|
|
2298
|
+
return this.#session.keyboardProtocol === 'kitty' || this.#receivedModifiedEnter.has(key);
|
|
2299
|
+
return true;
|
|
2300
|
+
})), 'alternate-send');
|
|
2233
2301
|
const renderState = this.#reveal === undefined
|
|
2234
2302
|
? this.#state
|
|
2235
2303
|
: revealStreamingAssistant(this.#state, this.#reveal.revealed);
|
|
@@ -2288,6 +2356,7 @@ export class LocalTui {
|
|
|
2288
2356
|
toolDetailsKey: keysForAction(this.#keybindings, 'toggle-tool-details'),
|
|
2289
2357
|
turnDetailsKey: keysForAction(this.#keybindings, 'toggle-tools'),
|
|
2290
2358
|
busyEnter: this.#queueEditNewer !== null || this.#queueEditPending ? 'queue' : this.#busyEnter,
|
|
2359
|
+
...(this.#queueEditNewer !== null || this.#queueEditPending || alternateSendKey === 'Disabled' ? {} : { alternateSendKey }),
|
|
2291
2360
|
steerKey: keysForAction(this.#keybindings, 'steer-turn'),
|
|
2292
2361
|
queueKey: keysForAction(this.#keybindings, 'follow-up'),
|
|
2293
2362
|
openedGroups: this.#openedGroups,
|
|
@@ -2323,7 +2392,8 @@ export class LocalTui {
|
|
|
2323
2392
|
this.#syncScroll(frame.transcript);
|
|
2324
2393
|
this.#jumpToLatest = frame.jumpToLatest;
|
|
2325
2394
|
this.#fileActionFrame = frame.transcript === undefined || frame.transientSurface === 'overlay' ? undefined : frame;
|
|
2326
|
-
this.#
|
|
2395
|
+
this.#inputFrame = frame.inputRows === undefined ? undefined : frame;
|
|
2396
|
+
this.#setMouseTracking(this.#inputFrame !== undefined || this.#fileBrowser !== null || this.#trajectory !== null || this.#documentMouseEnabled() || this.#promptChoices.length > 0 || this.#promptActions.length > 0 || (frame.transcript !== undefined && frame.transientSurface !== 'overlay'
|
|
2327
2397
|
&& (this.#inspecting || this.#maxStart > 0 || (frame.transcript.fileRegions?.length ?? 0) > 0 || (frame.transcript.disclosures?.length ?? 0) > 0)));
|
|
2328
2398
|
// A run opening or folding reshapes rows above the screen in place; the
|
|
2329
2399
|
// renderer's frozen boundary is a row index and has to be told, or a fold
|
|
@@ -2369,6 +2439,7 @@ export class LocalTui {
|
|
|
2369
2439
|
}
|
|
2370
2440
|
#scrollBy(delta) {
|
|
2371
2441
|
this.#workspacePress = undefined;
|
|
2442
|
+
this.#promptAnchor = undefined;
|
|
2372
2443
|
if (this.#follow && (delta > 0 || this.#maxStart === 0))
|
|
2373
2444
|
return;
|
|
2374
2445
|
if (delta === 0 && this.#maxStart === 0)
|
|
@@ -2392,6 +2463,7 @@ export class LocalTui {
|
|
|
2392
2463
|
this.#render();
|
|
2393
2464
|
}
|
|
2394
2465
|
#followTail() {
|
|
2466
|
+
this.#promptAnchor = undefined;
|
|
2395
2467
|
this.#follow = true;
|
|
2396
2468
|
this.#scrollStart = this.#maxStart;
|
|
2397
2469
|
this.#endInspection();
|
|
@@ -2479,6 +2551,7 @@ export class LocalTui {
|
|
|
2479
2551
|
* or go back to the live tail once nothing is open any more.
|
|
2480
2552
|
*/
|
|
2481
2553
|
#inspect(target) {
|
|
2554
|
+
this.#promptAnchor = undefined;
|
|
2482
2555
|
const open = this.#toolsExpanded || this.#openedGroups.size > 0 || this.#expandedOutputs.size > 0;
|
|
2483
2556
|
if (!open) {
|
|
2484
2557
|
this.#inspecting = false;
|
|
@@ -2814,6 +2887,40 @@ export class LocalTui {
|
|
|
2814
2887
|
this.#handleTrajectoryMouse(event);
|
|
2815
2888
|
return;
|
|
2816
2889
|
}
|
|
2890
|
+
const frameRow = this.#renderer.frameRowAt(event.row);
|
|
2891
|
+
const inputRow = this.#inputFrame?.inputRows?.find(row => row.row === frameRow);
|
|
2892
|
+
const completionRow = this.#inputFrame?.autocompleteRows?.find(row => row.row === frameRow);
|
|
2893
|
+
if (event.button === 'left') {
|
|
2894
|
+
const pressed = this.#inputPress;
|
|
2895
|
+
if (event.action === 'press' && !event.shift && (inputRow !== undefined || completionRow !== undefined)) {
|
|
2896
|
+
const completion = completionRow === undefined ? undefined : this.#ac?.items[completionRow.index];
|
|
2897
|
+
this.#inputPress = { row: event.row, column: event.column, text: this.#editor.text,
|
|
2898
|
+
...(inputRow === undefined ? {} : { cursor: inputRow.start + indexAtCell(inputRow.text, Math.max(0, event.column - inputRow.column)) }),
|
|
2899
|
+
...(completion === undefined ? {} : { completion }) };
|
|
2900
|
+
}
|
|
2901
|
+
else if (event.action === 'drag')
|
|
2902
|
+
this.#inputPress = undefined;
|
|
2903
|
+
else if (event.action === 'release') {
|
|
2904
|
+
this.#inputPress = undefined;
|
|
2905
|
+
if (pressed !== undefined && !event.shift && pressed.row === event.row && pressed.column === event.column && pressed.text === this.#editor.text) {
|
|
2906
|
+
this.#selection.clear();
|
|
2907
|
+
this.#processSelection = undefined;
|
|
2908
|
+
this.#subagentSelection = undefined;
|
|
2909
|
+
this.#transcriptSearch = null;
|
|
2910
|
+
if (pressed.completion !== undefined && this.#ac !== null && completionRow !== undefined && this.#ac.items[completionRow.index] === pressed.completion) {
|
|
2911
|
+
this.#ac.selected = completionRow.index;
|
|
2912
|
+
this.#applySelectedCompletion();
|
|
2913
|
+
}
|
|
2914
|
+
else if (pressed.cursor !== undefined && inputRow !== undefined
|
|
2915
|
+
&& pressed.cursor === inputRow.start + indexAtCell(inputRow.text, Math.max(0, event.column - inputRow.column))) {
|
|
2916
|
+
this.#editor.setCursor(pressed.cursor);
|
|
2917
|
+
this.#refreshAutocomplete();
|
|
2918
|
+
}
|
|
2919
|
+
this.#render();
|
|
2920
|
+
return;
|
|
2921
|
+
}
|
|
2922
|
+
}
|
|
2923
|
+
}
|
|
2817
2924
|
const promptAction = this.#promptActions.find(action => event.row === action.row && event.column >= action.column && event.column < action.column + action.width);
|
|
2818
2925
|
if (this.#promptActionPress !== undefined) {
|
|
2819
2926
|
const pressed = this.#promptActionPress;
|
|
@@ -3085,15 +3192,20 @@ export class LocalTui {
|
|
|
3085
3192
|
}
|
|
3086
3193
|
}
|
|
3087
3194
|
#canQuoteSelection() {
|
|
3088
|
-
return this.#prompt === null && this.#settings === null && this.#trajectory === null && this.#fileBrowser === null && this.#search === null
|
|
3195
|
+
return this.#prompt === null && this.#settings === null && this.#trajectory === null && (this.#fileBrowser === null || this.#fileBrowser.state.canQuote && !this.#fileBrowserOwnsInput()) && this.#search === null
|
|
3089
3196
|
&& this.#transcriptSearch === null && (this.#inspected === undefined || this.#inspected.writable === true);
|
|
3090
3197
|
}
|
|
3198
|
+
#fileBrowserOwnsInput() {
|
|
3199
|
+
const state = this.#fileBrowser?.state;
|
|
3200
|
+
return state !== undefined && (state.editing !== undefined || state.help || state.menu !== undefined);
|
|
3201
|
+
}
|
|
3091
3202
|
#quoteSelection() {
|
|
3092
3203
|
if (!this.#canQuoteSelection())
|
|
3093
3204
|
return;
|
|
3094
3205
|
const text = this.#selection.valid(this.#renderer.viewport()) ? this.#selection.text() : undefined;
|
|
3095
3206
|
this.#selection.clear();
|
|
3096
3207
|
if (text !== undefined) {
|
|
3208
|
+
this.#closeFileBrowser();
|
|
3097
3209
|
this.#appendQuote(text.split('\n').map(line => '> ' + line).join('\n'));
|
|
3098
3210
|
}
|
|
3099
3211
|
this.#render();
|
|
@@ -3216,6 +3328,9 @@ export class LocalTui {
|
|
|
3216
3328
|
}
|
|
3217
3329
|
if (this.#session.handle(event))
|
|
3218
3330
|
return;
|
|
3331
|
+
if (!this.#paste && event.type === 'key' && (event.id === 'ctrl+enter' || event.id === 'super+enter')) {
|
|
3332
|
+
this.#receivedModifiedEnter.add(event.id);
|
|
3333
|
+
}
|
|
3219
3334
|
if (event.type === 'focus') {
|
|
3220
3335
|
this.#terminalFocused = event.focused;
|
|
3221
3336
|
if (event.focused && this.#notificationPolicy === 'unfocused')
|
|
@@ -3232,17 +3347,22 @@ export class LocalTui {
|
|
|
3232
3347
|
this.#disclosurePress = undefined;
|
|
3233
3348
|
this.#mouseActionPress = undefined;
|
|
3234
3349
|
this.#workspacePress = undefined;
|
|
3350
|
+
this.#inputPress = undefined;
|
|
3235
3351
|
if (this.#keyTest !== undefined && this.#prompt?.request.signal === this.#keyTest.signal
|
|
3236
3352
|
&& (event.type === 'text' || event.type === 'paste-start' || event.type === 'paste-end' || event.type === 'key' && !['escape', 'ctrl+c'].includes(event.id))) {
|
|
3237
3353
|
this.#keyTest.observe(event);
|
|
3238
3354
|
return;
|
|
3239
3355
|
}
|
|
3240
3356
|
if (this.#selection.active) {
|
|
3241
|
-
if (event.type === 'key' && this.#boundAction(event.id) === 'quote-selection') {
|
|
3357
|
+
if (event.type === 'key' && this.#boundAction(event.id) === 'quote-selection' && this.#canQuoteSelection()) {
|
|
3358
|
+
this.#quoteSelection();
|
|
3359
|
+
return;
|
|
3360
|
+
}
|
|
3361
|
+
if (this.#fileBrowser !== null && event.type === 'text' && event.value.toLowerCase() === 'q' && this.#canQuoteSelection()) {
|
|
3242
3362
|
this.#quoteSelection();
|
|
3243
3363
|
return;
|
|
3244
3364
|
}
|
|
3245
|
-
if (event.type === 'key' && (
|
|
3365
|
+
if (event.type === 'key' && (event.id === 'super+c' || event.id === 'enter' && !this.#fileBrowserOwnsInput() || this.#boundAction(event.id) === 'copy-prompt')) {
|
|
3246
3366
|
void this.#copySelection();
|
|
3247
3367
|
return;
|
|
3248
3368
|
}
|
|
@@ -3262,6 +3382,11 @@ export class LocalTui {
|
|
|
3262
3382
|
this.#applyTrajectoryInput(event);
|
|
3263
3383
|
return;
|
|
3264
3384
|
}
|
|
3385
|
+
if (this.#ac !== null && this.#inputFrame?.autocompleteRows?.some(row => row.row === this.#renderer.frameRowAt(event.row ?? -1))) {
|
|
3386
|
+
this.#ac.selected = nextSelectableAutocompleteIndex(this.#ac.items, this.#ac.selected + (event.direction === 'up' ? -1 : 1), event.direction === 'up' ? -1 : 1);
|
|
3387
|
+
this.#render();
|
|
3388
|
+
return;
|
|
3389
|
+
}
|
|
3265
3390
|
this.#jumpPress = undefined;
|
|
3266
3391
|
if (this.#mouseTracking && !this.#paste && this.#pasteInFlight === 0) {
|
|
3267
3392
|
const step = wheelStep(this.#wheelGesture, Date.now(), event.direction === 'up' ? -1 : 1, !this.#wheelAccelerates);
|
|
@@ -3408,7 +3533,7 @@ export class LocalTui {
|
|
|
3408
3533
|
this.#render();
|
|
3409
3534
|
return;
|
|
3410
3535
|
}
|
|
3411
|
-
if (event.type === 'key' && (event.id === 'ctrl+-' || event.id === 'ctrl+_')
|
|
3536
|
+
if (event.type === 'key' && (event.id === 'ctrl+-' || event.id === 'ctrl+_' || event.id === 'super+z')
|
|
3412
3537
|
&& this.#clearedDraft !== undefined && this.#clearedDraft.revision === this.#editor.revision
|
|
3413
3538
|
&& !this.#hasComposerContent()) {
|
|
3414
3539
|
const cleared = this.#clearedDraft.draft;
|
|
@@ -4737,6 +4862,11 @@ export class LocalTui {
|
|
|
4737
4862
|
this.#queuedSubmissions.push(submission);
|
|
4738
4863
|
this.#pendingInputs.flush(this.#pendingScope());
|
|
4739
4864
|
}
|
|
4865
|
+
#resolveDelivery(delivery) {
|
|
4866
|
+
if (delivery === 'alternate')
|
|
4867
|
+
return this.#busyEnter === 'queue' ? 'steer' : 'queue';
|
|
4868
|
+
return delivery ?? this.#busyEnter;
|
|
4869
|
+
}
|
|
4740
4870
|
#submit(text, delivery) {
|
|
4741
4871
|
if (this.#prompt !== null) {
|
|
4742
4872
|
if (this.#prompt.request.presentation === 'progress')
|
|
@@ -4758,7 +4888,7 @@ export class LocalTui {
|
|
|
4758
4888
|
submission.literal = true;
|
|
4759
4889
|
const command = !submission.literal && (submittedText.trimStart().startsWith('/') || (images.length === 0 && files.length === 0 && submittedText.startsWith('!')));
|
|
4760
4890
|
if (!command && this.#state.status === 'running') {
|
|
4761
|
-
submission.delivery = queueEditNewer !== null || this.#queueEditPending ? 'queue' :
|
|
4891
|
+
submission.delivery = queueEditNewer !== null || this.#queueEditPending ? 'queue' : this.#resolveDelivery(delivery);
|
|
4762
4892
|
}
|
|
4763
4893
|
if (historyText !== '' && this.#history[this.#history.length - 1] !== historyText) {
|
|
4764
4894
|
this.#history.push(historyText);
|
|
@@ -4806,7 +4936,7 @@ export class LocalTui {
|
|
|
4806
4936
|
const newer = this.#queueEditNewer;
|
|
4807
4937
|
if (delivery === 'steer')
|
|
4808
4938
|
submission.literal = true;
|
|
4809
|
-
submission.delivery = newer !== null || this.#queueEditPending
|
|
4939
|
+
submission.delivery = newer !== null || this.#queueEditPending || this.#state.status !== 'running' ? 'queue' : this.#resolveDelivery(delivery);
|
|
4810
4940
|
const { images, files = [], literal = false, text: submittedText } = submission;
|
|
4811
4941
|
if (historyText !== '' && this.#history[this.#history.length - 1] !== historyText) {
|
|
4812
4942
|
this.#history.push(historyText);
|
|
@@ -5179,10 +5309,11 @@ export class LocalTui {
|
|
|
5179
5309
|
}, 25);
|
|
5180
5310
|
}
|
|
5181
5311
|
#runHelp(full) {
|
|
5312
|
+
const enhancedKeys = this.#session.keyboardProtocol === 'kitty';
|
|
5182
5313
|
if (!this.#tty) {
|
|
5183
|
-
this.commandOutput('help', ['Help', '', '## Essential Shortcuts', '', formatEssentialHotkeysText(this.#keybindings),
|
|
5314
|
+
this.commandOutput('help', ['Help', '', '## Essential Shortcuts', '', formatEssentialHotkeysText(this.#keybindings, enhancedKeys),
|
|
5184
5315
|
'', formatHelpText(this.#commands(), resolveUiLanguage(this.#uiLanguage)),
|
|
5185
|
-
...(full ? ['', formatHotkeysText(this.#keybindings)] : ['', 'Use `/help full` to view every keyboard shortcut.'])].join('\n'));
|
|
5316
|
+
...(full ? ['', formatHotkeysText(this.#keybindings, enhancedKeys)] : ['', 'Use `/help full` to view every keyboard shortcut.'])].join('\n'));
|
|
5186
5317
|
return;
|
|
5187
5318
|
}
|
|
5188
5319
|
if (this.#fileReviewAbort !== undefined || this.#prompt !== null)
|
|
@@ -5194,7 +5325,7 @@ export class LocalTui {
|
|
|
5194
5325
|
copyText: (text, label) => this.copyText(text, label),
|
|
5195
5326
|
insertText: (text) => this.insertText(text),
|
|
5196
5327
|
};
|
|
5197
|
-
void browseHelp(surface, this.#commands(), this.#keybindings, resolveUiLanguage(this.#uiLanguage), full, abort.signal)
|
|
5328
|
+
void browseHelp(surface, this.#commands(), this.#keybindings, resolveUiLanguage(this.#uiLanguage), full, abort.signal, enhancedKeys)
|
|
5198
5329
|
.catch(error => { if (!abort.signal.aborted && !this.#disposed)
|
|
5199
5330
|
this.notice(String(error), { level: 'error' }); })
|
|
5200
5331
|
.finally(() => { if (this.#fileReviewAbort === abort)
|
|
@@ -5250,6 +5381,19 @@ export class LocalTui {
|
|
|
5250
5381
|
// closes. Claiming it here would skip that layering.
|
|
5251
5382
|
if (action === 'interrupt')
|
|
5252
5383
|
return false;
|
|
5384
|
+
if (action === 'alternate-send') {
|
|
5385
|
+
if (this.#settings !== null || this.#search !== null)
|
|
5386
|
+
return false;
|
|
5387
|
+
if (this.#inspected !== undefined && !this.#inspected.writable)
|
|
5388
|
+
return true;
|
|
5389
|
+
if (this.#handleAutocomplete({ type: 'key', id: 'enter' }))
|
|
5390
|
+
return true;
|
|
5391
|
+
if (this.#hasComposerContent())
|
|
5392
|
+
this.#submit(this.#editor.expandedText, 'alternate');
|
|
5393
|
+
else
|
|
5394
|
+
this.#render();
|
|
5395
|
+
return true;
|
|
5396
|
+
}
|
|
5253
5397
|
if (action === 'clear-composer')
|
|
5254
5398
|
return this.#runClearComposer();
|
|
5255
5399
|
if (action === 'quit') {
|
|
@@ -5416,6 +5560,33 @@ export class LocalTui {
|
|
|
5416
5560
|
this.#scrollBy(-this.#pageSize());
|
|
5417
5561
|
return true;
|
|
5418
5562
|
}
|
|
5563
|
+
if (action === 'previous-turn' || action === 'next-turn') {
|
|
5564
|
+
if (this.#search !== null)
|
|
5565
|
+
return false;
|
|
5566
|
+
const prompts = this.#state.blocks.flatMap((block, index) => block.kind === 'user' ? [index] : []);
|
|
5567
|
+
const anchor = this.#promptAnchor === undefined ? -1 : this.#state.blocks.indexOf(this.#promptAnchor);
|
|
5568
|
+
const top = this.#follow
|
|
5569
|
+
? this.#renderer.viewport().map((_, row) => this.#renderer.documentRowAt(row)).find(row => row !== undefined) ?? this.#scrollStart
|
|
5570
|
+
: this.#scrollStart;
|
|
5571
|
+
const target = action === 'previous-turn'
|
|
5572
|
+
? prompts.findLast(index => anchor >= 0 ? index < anchor : (this.#blockStarts[index] ?? 0) < top)
|
|
5573
|
+
: prompts.find(index => anchor >= 0 ? index > anchor : (this.#blockStarts[index] ?? 0) > top);
|
|
5574
|
+
this.#processSelection = undefined;
|
|
5575
|
+
this.#ac = null;
|
|
5576
|
+
if (target === undefined) {
|
|
5577
|
+
if (action === 'next-turn')
|
|
5578
|
+
this.#followTail();
|
|
5579
|
+
}
|
|
5580
|
+
else {
|
|
5581
|
+
this.#promptAnchor = this.#state.blocks[target];
|
|
5582
|
+
this.#inspecting = true;
|
|
5583
|
+
this.#follow = false;
|
|
5584
|
+
this.#focusBlock = target;
|
|
5585
|
+
this.#focusBlockEdge = 'start';
|
|
5586
|
+
}
|
|
5587
|
+
this.#render();
|
|
5588
|
+
return true;
|
|
5589
|
+
}
|
|
5419
5590
|
if (action === 'scroll-page-down') {
|
|
5420
5591
|
if (this.#search !== null)
|
|
5421
5592
|
return false;
|
|
@@ -5711,7 +5882,7 @@ export class LocalTui {
|
|
|
5711
5882
|
});
|
|
5712
5883
|
if (entered === null || this.#disposed)
|
|
5713
5884
|
continue;
|
|
5714
|
-
const normalized = entered
|
|
5885
|
+
const normalized = normalizeKeyId(entered);
|
|
5715
5886
|
if (normalized === '')
|
|
5716
5887
|
continue;
|
|
5717
5888
|
const next = { ...this.#keybindings };
|
|
@@ -5846,6 +6017,7 @@ export class LocalTui {
|
|
|
5846
6017
|
return true;
|
|
5847
6018
|
}
|
|
5848
6019
|
if (event.id === 'home') {
|
|
6020
|
+
this.#promptAnchor = undefined;
|
|
5849
6021
|
const current = this.#fileActionFrame?.transcript?.disclosures?.findLast(target => target.detailEnd !== undefined && this.#scrollStart > target.row && this.#scrollStart < target.detailEnd);
|
|
5850
6022
|
this.#processSelection = current?.block ?? selected;
|
|
5851
6023
|
this.#focusBlock = this.#processSelection;
|
|
@@ -5854,6 +6026,7 @@ export class LocalTui {
|
|
|
5854
6026
|
return true;
|
|
5855
6027
|
}
|
|
5856
6028
|
if (event.id === 'up' || event.id === 'down') {
|
|
6029
|
+
this.#promptAnchor = undefined;
|
|
5857
6030
|
const rows = this.#processRows();
|
|
5858
6031
|
const at = Math.max(0, rows.indexOf(selected));
|
|
5859
6032
|
const next = rows[Math.max(0, Math.min(rows.length - 1, at + (event.id === 'up' ? -1 : 1)))];
|
|
@@ -6040,12 +6213,18 @@ export class LocalTui {
|
|
|
6040
6213
|
return;
|
|
6041
6214
|
}
|
|
6042
6215
|
this.#transcriptSearch = command.state;
|
|
6043
|
-
if (command.kind === 'focus')
|
|
6044
|
-
this.#
|
|
6045
|
-
this.#focusBlockEdge = 'start';
|
|
6046
|
-
}
|
|
6216
|
+
if (command.kind === 'focus')
|
|
6217
|
+
this.#focusSearchMatch(command.match);
|
|
6047
6218
|
this.#render();
|
|
6048
6219
|
}
|
|
6220
|
+
#focusSearchMatch(match) {
|
|
6221
|
+
this.#promptAnchor = undefined;
|
|
6222
|
+
this.#processSelection = undefined;
|
|
6223
|
+
this.#inspecting = true;
|
|
6224
|
+
this.#follow = false;
|
|
6225
|
+
this.#focusBlock = match.block;
|
|
6226
|
+
this.#focusBlockEdge = 'start';
|
|
6227
|
+
}
|
|
6049
6228
|
}
|
|
6050
6229
|
/**
|
|
6051
6230
|
* Mount the local terminal provider as the tui service.
|