@linxiraos/pi-tui 1.2.1 → 1.3.1
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/CHANGELOG.md +245 -224
- package/README.md +36 -0
- package/THIRD-PARTY-NOTICES.txt +55 -5
- package/dist/types/app-keybindings.d.ts +18 -8
- package/dist/types/apps/git/diff-pane.d.ts +5 -0
- package/dist/types/apps/git/git-tui.d.ts +16 -2
- package/dist/types/apps/git/state.d.ts +11 -0
- package/dist/types/autocomplete.d.ts +2 -0
- package/dist/types/charts/chart-svg.d.ts +7 -13
- package/dist/types/chat/advisor-message.d.ts +4 -1
- package/dist/types/chat/assistant-message.d.ts +9 -4
- package/dist/types/chat/figure-markdown.d.ts +7 -2
- package/dist/types/chat/messages.d.ts +3 -1
- package/dist/types/chat/read-tool-group.d.ts +2 -2
- package/dist/types/chat/table-chart.d.ts +11 -15
- package/dist/types/chat/tool-execution.d.ts +32 -20
- package/dist/types/chat/user-message.d.ts +3 -2
- package/dist/types/chrome/countdown-timer.d.ts +3 -2
- package/dist/types/chrome/diff.d.ts +2 -3
- package/dist/types/chrome/status-notice.d.ts +2 -0
- package/dist/types/chrome/transcript-container.d.ts +2 -0
- package/dist/types/components/image.d.ts +2 -0
- package/dist/types/components/markdown.d.ts +49 -3
- package/dist/types/components/text.d.ts +2 -0
- package/dist/types/debug-server.d.ts +2 -0
- package/dist/types/keybinding-matchers.d.ts +6 -0
- package/dist/types/multiplexers/tmux.d.ts +1 -0
- package/dist/types/multiplexers/types.d.ts +6 -0
- package/dist/types/native/backend.d.ts +5 -4
- package/dist/types/native/blobs.d.ts +24 -12
- package/dist/types/overlays/agent-hub-projection.d.ts +10 -5
- package/dist/types/overlays/annotation-overlay.d.ts +19 -0
- package/dist/types/overlays/approval-dialog.d.ts +79 -0
- package/dist/types/overlays/ask-dialog.d.ts +11 -6
- package/dist/types/overlays/btw-history-panel.d.ts +2 -6
- package/dist/types/overlays/hook-editor.d.ts +7 -2
- package/dist/types/overlays/hook-input.d.ts +12 -4
- package/dist/types/overlays/job-tree.d.ts +50 -0
- package/dist/types/overlays/jobs-panel.d.ts +25 -11
- package/dist/types/overlays/login-dialog.d.ts +8 -3
- package/dist/types/overlays/mcp-add-wizard.d.ts +9 -1
- package/dist/types/overlays/model-browser.d.ts +6 -1
- package/dist/types/overlays/model-picker.d.ts +2 -0
- package/dist/types/overlays/pause-screen.d.ts +5 -0
- package/dist/types/overlays/plan-review-overlay.d.ts +4 -0
- package/dist/types/overlays/report-panel.d.ts +5 -1
- package/dist/types/overlays/reset-usage-selector.d.ts +7 -5
- package/dist/types/overlays/session-account-selector.d.ts +4 -0
- package/dist/types/overlays/session-info-overlay.d.ts +7 -3
- package/dist/types/overlays/session-selector.d.ts +3 -1
- package/dist/types/overlays/usage-dashboard.d.ts +6 -6
- package/dist/types/prompt/composer.d.ts +23 -20
- package/dist/types/prompt/custom-editor.d.ts +20 -10
- package/dist/types/prompt/image-references.d.ts +22 -0
- package/dist/types/prompt/internal-url-autocomplete.d.ts +1 -0
- package/dist/types/prompt/macos-spelling.d.ts +2 -1
- package/dist/types/prompt/prose-gate.d.ts +3 -0
- package/dist/types/prompt/queued-messages.d.ts +1 -1
- package/dist/types/prompt/typing-word.d.ts +25 -0
- package/dist/types/prompt/welcome.d.ts +26 -6
- package/dist/types/render/hanging-wrap.d.ts +7 -0
- package/dist/types/render/hyperlink.d.ts +2 -0
- package/dist/types/render/render-utils.d.ts +9 -1
- package/dist/types/render/utils.d.ts +1 -1
- package/dist/types/setup/scenes/splash.d.ts +6 -5
- package/dist/types/status-line/context-usage.d.ts +4 -1
- package/dist/types/status-line/host.d.ts +6 -4
- package/dist/types/status-line/startup.d.ts +6 -6
- package/dist/types/status-line/types.d.ts +3 -0
- package/dist/types/stdin-buffer.d.ts +7 -0
- package/dist/types/terminal-capabilities.d.ts +16 -1
- package/dist/types/terminal-multiplexer.d.ts +7 -0
- package/dist/types/terminal.d.ts +8 -4
- package/dist/types/theme/symbols.d.ts +1 -1
- package/dist/types/theme/theme.d.ts +11 -0
- package/dist/types/thinking.d.ts +5 -0
- package/dist/types/tmux.d.ts +39 -4
- package/dist/types/tools/ast-edit.d.ts +2 -2
- package/dist/types/tools/ast-grep.d.ts +2 -2
- package/dist/types/tools/bash.d.ts +6 -1
- package/dist/types/tools/edit.d.ts +2 -0
- package/dist/types/tools/eval.d.ts +53 -4
- package/dist/types/tools/find.d.ts +14 -4
- package/dist/types/tools/glob.d.ts +3 -3
- package/dist/types/tools/grep.d.ts +2 -2
- package/dist/types/tools/grouped-file-output.d.ts +9 -4
- package/dist/types/tools/native-view.d.ts +1 -1
- package/dist/types/tools/read.d.ts +32 -10
- package/dist/types/tools/renderer.d.ts +2 -0
- package/dist/types/tools/task.d.ts +31 -0
- package/dist/types/tools/web-search-types.d.ts +1 -0
- package/dist/types/tools/write.d.ts +2 -0
- package/dist/types/tui.d.ts +20 -3
- package/package.json +10 -10
- package/src/app-keybindings.d.ts +18 -8
- package/src/app-keybindings.ts +20 -8
- package/src/apps/autoresearch-dashboard.ts +444 -27
- package/src/apps/debug/raw-sse.ts +2 -2
- package/src/apps/git/diff-pane.ts +39 -41
- package/src/apps/git/git-tui.ts +80 -14
- package/src/apps/git/sidebar.ts +10 -1
- package/src/apps/git/state.ts +12 -0
- package/src/autocomplete.d.ts +2 -0
- package/src/autocomplete.ts +21 -19
- package/src/bracketed-paste.ts +9 -2
- package/src/charts/chart-svg.d.ts +7 -13
- package/src/charts/chart-svg.ts +19 -22
- package/src/charts/table-data.ts +17 -6
- package/src/chat/advisor-message.d.ts +4 -1
- package/src/chat/advisor-message.ts +77 -24
- package/src/chat/assistant-message.d.ts +9 -4
- package/src/chat/assistant-message.ts +127 -31
- package/src/chat/bash-execution.ts +7 -2
- package/src/chat/figure-markdown.d.ts +7 -2
- package/src/chat/figure-markdown.ts +10 -3
- package/src/chat/image-loading.ts +9 -6
- package/src/chat/messages.d.ts +3 -1
- package/src/chat/messages.ts +4 -1
- package/src/chat/read-tool-group.d.ts +2 -2
- package/src/chat/read-tool-group.ts +97 -42
- package/src/chat/svg-figure.ts +3 -1
- package/src/chat/svg-source.ts +6 -3
- package/src/chat/table-chart.d.ts +11 -15
- package/src/chat/table-chart.ts +24 -57
- package/src/chat/tool-execution.d.ts +32 -20
- package/src/chat/tool-execution.ts +103 -36
- package/src/chat/user-message.d.ts +3 -2
- package/src/chat/user-message.ts +54 -34
- package/src/chrome/countdown-timer.ts +3 -3
- package/src/chrome/diff.d.ts +2 -3
- package/src/chrome/diff.ts +4 -9
- package/src/chrome/status-notice.ts +5 -0
- package/src/chrome/transcript-container.d.ts +2 -0
- package/src/chrome/transcript-container.ts +212 -28
- package/src/components/editor.ts +12 -12
- package/src/components/image.d.ts +2 -0
- package/src/components/image.ts +54 -20
- package/src/components/markdown.d.ts +49 -3
- package/src/components/markdown.ts +482 -12
- package/src/components/settings-list.ts +2 -4
- package/src/components/text.d.ts +2 -0
- package/src/components/text.ts +11 -1
- package/src/debug-server.d.ts +2 -0
- package/src/debug-server.ts +51 -69
- package/src/hotkeys-markdown.ts +2 -0
- package/src/keybinding-matchers.d.ts +6 -0
- package/src/keybinding-matchers.ts +19 -0
- package/src/multiplexers/tmux.d.ts +1 -0
- package/src/multiplexers/tmux.ts +3 -0
- package/src/multiplexers/types.d.ts +6 -0
- package/src/multiplexers/types.ts +6 -0
- package/src/native/backend.d.ts +5 -4
- package/src/native/backend.ts +13 -5
- package/src/native/blobs.d.ts +24 -12
- package/src/native/blobs.ts +56 -7
- package/src/native/reconcile.ts +43 -21
- package/src/overlays/advisor-config.ts +259 -8
- package/src/overlays/agent-hub-projection.d.ts +10 -5
- package/src/overlays/agent-hub-projection.ts +63 -12
- package/src/overlays/annotation-overlay.ts +422 -168
- package/src/overlays/approval-dialog.ts +389 -0
- package/src/overlays/ask-dialog.ts +244 -105
- package/src/overlays/btw-history-panel.ts +208 -147
- package/src/overlays/extensions/extension-list.ts +4 -6
- package/src/overlays/hook-editor.ts +90 -32
- package/src/overlays/hook-input.ts +105 -19
- package/src/overlays/job-tree.ts +177 -0
- package/src/overlays/jobs-panel.ts +222 -142
- package/src/overlays/login-dialog.ts +201 -91
- package/src/overlays/mcp-add-wizard.ts +562 -279
- package/src/overlays/model-browser.ts +32 -7
- package/src/overlays/model-hub.ts +4 -10
- package/src/overlays/model-picker.ts +58 -21
- package/src/overlays/pause-screen.ts +81 -23
- package/src/overlays/plan-review-overlay.ts +16 -0
- package/src/overlays/plugin-selector.ts +17 -1
- package/src/overlays/queue-mode-selector.ts +10 -0
- package/src/overlays/report-panel.ts +6 -2
- package/src/overlays/reset-usage-selector.ts +130 -21
- package/src/overlays/session-account-selector.ts +36 -0
- package/src/overlays/session-info-overlay.ts +199 -76
- package/src/overlays/session-selector.ts +15 -10
- package/src/overlays/settings-selector.ts +3 -0
- package/src/overlays/show-images-selector.ts +1 -0
- package/src/overlays/snapcompact-shape-preview.ts +13 -6
- package/src/overlays/theme-selector.ts +6 -1
- package/src/overlays/thinking-selector.ts +8 -4
- package/src/overlays/tree-selector.ts +117 -2
- package/src/overlays/usage-dashboard.ts +498 -144
- package/src/prompt/composer-cache.ts +4 -1
- package/src/prompt/composer.ts +200 -74
- package/src/prompt/custom-editor.ts +168 -65
- package/src/prompt/image-references.d.ts +22 -0
- package/src/prompt/image-references.ts +79 -2
- package/src/prompt/internal-url-autocomplete.ts +1 -0
- package/src/prompt/macos-spelling.ts +28 -5
- package/src/prompt/prose-gate.ts +3 -0
- package/src/prompt/queued-messages.ts +9 -2
- package/src/prompt/tips.txt +1 -2
- package/src/prompt/typing-word.ts +59 -0
- package/src/prompt/welcome.ts +97 -45
- package/src/render/code-cell.ts +18 -5
- package/src/render/hanging-wrap.d.ts +7 -0
- package/src/render/hanging-wrap.ts +58 -0
- package/src/render/hyperlink.d.ts +2 -0
- package/src/render/hyperlink.ts +5 -0
- package/src/render/render-utils.d.ts +9 -1
- package/src/render/render-utils.ts +16 -2
- package/src/render/utils.d.ts +1 -1
- package/src/render/utils.ts +1 -1
- package/src/render/width-aware-text.ts +6 -5
- package/src/setup/scenes/splash.ts +22 -8
- package/src/status-line/component.ts +79 -11
- package/src/status-line/context-usage.ts +176 -73
- package/src/status-line/footer.ts +18 -35
- package/src/status-line/host.ts +6 -1
- package/src/status-line/segments.ts +16 -5
- package/src/status-line/startup.ts +6 -6
- package/src/status-line/types.ts +1 -0
- package/src/stdin-buffer.d.ts +7 -0
- package/src/stdin-buffer.ts +300 -62
- package/src/terminal-capabilities.d.ts +16 -1
- package/src/terminal-capabilities.ts +66 -28
- package/src/terminal-multiplexer.d.ts +7 -0
- package/src/terminal-multiplexer.ts +14 -0
- package/src/terminal.d.ts +8 -4
- package/src/terminal.ts +190 -63
- package/src/theme/symbols.d.ts +1 -1
- package/src/theme/symbols.ts +1 -0
- package/src/theme/theme-class.ts +1 -0
- package/src/theme/theme.d.ts +11 -0
- package/src/theme/theme.ts +46 -14
- package/src/thinking.d.ts +5 -0
- package/src/thinking.ts +9 -1
- package/src/tmux.d.ts +39 -4
- package/src/tmux.ts +110 -16
- package/src/tools/ask.ts +20 -9
- package/src/tools/ast-edit.d.ts +2 -2
- package/src/tools/ast-edit.ts +30 -7
- package/src/tools/ast-grep.d.ts +2 -2
- package/src/tools/ast-grep.ts +39 -35
- package/src/tools/bash.d.ts +6 -1
- package/src/tools/bash.ts +15 -9
- package/src/tools/debug.ts +2 -1
- package/src/tools/default-renderer.ts +3 -2
- package/src/tools/edit.d.ts +2 -0
- package/src/tools/edit.ts +100 -21
- package/src/tools/eval.d.ts +53 -4
- package/src/tools/eval.ts +173 -137
- package/src/tools/find.d.ts +14 -4
- package/src/tools/find.ts +71 -18
- package/src/tools/glob.d.ts +3 -3
- package/src/tools/glob.ts +51 -17
- package/src/tools/grep.d.ts +2 -2
- package/src/tools/grep.ts +99 -22
- package/src/tools/grouped-file-output.d.ts +9 -4
- package/src/tools/grouped-file-output.ts +21 -6
- package/src/tools/lsp.ts +30 -18
- package/src/tools/mcp.ts +2 -2
- package/src/tools/native-view.d.ts +1 -1
- package/src/tools/native-view.ts +2 -2
- package/src/tools/read.d.ts +32 -10
- package/src/tools/read.ts +89 -43
- package/src/tools/renderer.d.ts +2 -0
- package/src/tools/renderer.ts +2 -0
- package/src/tools/task.d.ts +31 -0
- package/src/tools/task.ts +40 -7
- package/src/tools/todo.ts +3 -2
- package/src/tools/wait.ts +16 -30
- package/src/tools/web-search-types.d.ts +1 -0
- package/src/tools/web-search-types.ts +1 -0
- package/src/tools/web-search.ts +2 -1
- package/src/tools/write.d.ts +2 -0
- package/src/tools/write.ts +36 -12
- package/src/tools/yield.ts +2 -2
- package/src/tui.d.ts +20 -3
- package/src/tui.ts +160 -43
- package/src/utils.ts +5 -3
- package/dist/types/tools/eval-format/code-mode-declarations.d.ts +0 -5
- package/src/tools/eval-format/code-mode-declarations.ts +0 -61
package/src/terminal.ts
CHANGED
|
@@ -25,12 +25,13 @@ import {
|
|
|
25
25
|
NotifyProtocol,
|
|
26
26
|
setCellDimensions,
|
|
27
27
|
setOsc99Supported,
|
|
28
|
+
setTerminalBackgroundColor,
|
|
28
29
|
setTerminalGlyphProtocol,
|
|
29
30
|
TERMINAL,
|
|
30
31
|
} from "./terminal-capabilities";
|
|
31
32
|
import { tmuxMultiplexer } from "./multiplexers/tmux";
|
|
32
33
|
import type { TerminalMultiplexerBackgroundColorCache } from "./multiplexers/types";
|
|
33
|
-
import { terminalMultiplexerSessions } from "./terminal-multiplexer";
|
|
34
|
+
import { classifyTerminalMultiplexerModule, terminalMultiplexerSessions } from "./terminal-multiplexer";
|
|
34
35
|
import { setTmuxPaneApcTitles, tmuxSessionSpeaksTsp } from "./tmux";
|
|
35
36
|
import { setHangulCompatibilityJamoWidth } from "./utils";
|
|
36
37
|
import { translateWindowsAltGrSequence } from "./windows-altgr";
|
|
@@ -55,6 +56,38 @@ const TSP_OSC_PREFIX = `\x1b]877;${TSP_PREFIX.slice(2)}`;
|
|
|
55
56
|
/** How long a TSP hello waits for its reply inside tmux, which answers DA1 itself. */
|
|
56
57
|
const TSP_TMUX_HELLO_TIMEOUT_MS = 1000;
|
|
57
58
|
|
|
59
|
+
/** Tern names itself in `TERM_PROGRAM`; a multiplexer started in a Tern pane may leave it behind. */
|
|
60
|
+
function isTernPane(env: NodeJS.ProcessEnv): boolean {
|
|
61
|
+
return env.TERM_PROGRAM?.toLowerCase() === "tern";
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Why a TSP `hello` cannot reach the terminal from this environment, or
|
|
66
|
+
* `null` when nothing in between is known to drop APC.
|
|
67
|
+
*
|
|
68
|
+
* In a Tern pane only a multiplexer's session marker counts: Tern sets
|
|
69
|
+
* `TERM=xterm-kitty`, so a `screen*`/`tmux*` TERM next to `TERM_PROGRAM=tern`
|
|
70
|
+
* without a marker was rewritten (shell rc, dev shell, `.env`), not set by a
|
|
71
|
+
* multiplexer. Elsewhere the TERM fallback keeps classifying sessions whose
|
|
72
|
+
* markers were stripped.
|
|
73
|
+
*
|
|
74
|
+
* devenv (2.0+) runs `devenv shell` in its own PTY and redraws the pane from a
|
|
75
|
+
* terminal emulator to keep its reload status line: it forwards DA1 to the
|
|
76
|
+
* real terminal but drops APC, so the DA1 reply always wins. It exports
|
|
77
|
+
* `DEVENV_RELOAD_FILE` only into that reloading shell; `devenv shell
|
|
78
|
+
* --no-reload` runs the shell on the real terminal.
|
|
79
|
+
*/
|
|
80
|
+
function tspPathBlocker(env: NodeJS.ProcessEnv): string | null {
|
|
81
|
+
const multiplexer = isTernPane(env)
|
|
82
|
+
? terminalMultiplexerSessions(env).find(session => session.ownsScreenGrid)
|
|
83
|
+
: classifyTerminalMultiplexerModule(env);
|
|
84
|
+
if (multiplexer?.ownsScreenGrid) return `inside a ${multiplexer.id} session, which swallows APC`;
|
|
85
|
+
if (env.DEVENV_RELOAD_FILE) {
|
|
86
|
+
return "inside devenv's reloading shell, which redraws the pane and drops APC; `devenv shell --no-reload` avoids it";
|
|
87
|
+
}
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
|
|
58
91
|
/**
|
|
59
92
|
* The TSP message a reassembled input string holds, in APC form, or
|
|
60
93
|
* `undefined` while its terminator has not arrived. An OSC 877 message may end
|
|
@@ -133,9 +166,22 @@ export function chunkForConPTY(data: string, maxChunkBytes: number = MAX_CONPTY_
|
|
|
133
166
|
let pos = 0;
|
|
134
167
|
while (pos < len) {
|
|
135
168
|
let bytes = 0;
|
|
136
|
-
// Index just past the most recent
|
|
137
|
-
// the natural cut point that leaves escape sequences
|
|
169
|
+
// Index just past the most recent line terminator we have consumed
|
|
170
|
+
// inside [pos, i): the natural cut point that leaves escape sequences
|
|
171
|
+
// and rows intact.
|
|
172
|
+
//
|
|
173
|
+
// Both terminators must be tracked. A full paint separates rows with
|
|
174
|
+
// `\r\n` and a newline alone is enough. A *diff* paint does not: TUI's
|
|
175
|
+
// row diff (`TUI#renderProviderFrame`, the `diffable` branch) addresses
|
|
176
|
+
// every changed row with `CSI row;1H` and closes it with a bare `\r`,
|
|
177
|
+
// emitting no `\n` anywhere in the frame. A newline-only tracker
|
|
178
|
+
// therefore finds no boundary in a diff paint and falls through to the
|
|
179
|
+
// hard cut at `i`, slicing the buffer through the middle of a row's
|
|
180
|
+
// text. Every boundary between two `process.stdout.write` calls is a
|
|
181
|
+
// point a foreign writer can inject into, so a hard cut lets unrelated
|
|
182
|
+
// output land inside a row instead of between rows.
|
|
138
183
|
let lastNewlineEnd = -1;
|
|
184
|
+
let lastCarriageReturnEnd = -1;
|
|
139
185
|
let i = pos;
|
|
140
186
|
while (i < len) {
|
|
141
187
|
const cu = data.charCodeAt(i);
|
|
@@ -161,16 +207,25 @@ export function chunkForConPTY(data: string, maxChunkBytes: number = MAX_CONPTY_
|
|
|
161
207
|
cuBytes = 3;
|
|
162
208
|
}
|
|
163
209
|
if (bytes + cuBytes > maxChunkBytes && i > pos) {
|
|
164
|
-
//
|
|
165
|
-
//
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
210
|
+
// Cut at the last line terminator. A `\r` that opens a `\r\n` pair is
|
|
211
|
+
// never tracked (see below), so the pair always stays in one
|
|
212
|
+
// `WriteFile` call. Falls back to the code-point boundary `i` only
|
|
213
|
+
// when the window holds no terminator at all (a single row longer
|
|
214
|
+
// than the cap), which still makes forward progress.
|
|
215
|
+
const cut = Math.max(lastNewlineEnd, lastCarriageReturnEnd);
|
|
216
|
+
let end = cut > pos ? cut : i;
|
|
217
|
+
// A hard cut landing between `\r` and `\n` moves back before the `\r`.
|
|
218
|
+
if (end === i && data.charCodeAt(i) === 0x0a && data.charCodeAt(i - 1) === 0x0d && i - 1 > pos) end = i - 1;
|
|
219
|
+
chunks.push(data.slice(pos, end));
|
|
220
|
+
pos = end;
|
|
169
221
|
break;
|
|
170
222
|
}
|
|
171
223
|
bytes += cuBytes;
|
|
172
224
|
i += cuLen;
|
|
225
|
+
// A `\r` directly followed by `\n` is half of a `\r\n` pair, even when the
|
|
226
|
+
// `\n` lies past the cap, so it is never a cut point on its own.
|
|
173
227
|
if (cu === 0x0a) lastNewlineEnd = i;
|
|
228
|
+
else if (cu === 0x0d && data.charCodeAt(i) !== 0x0a) lastCarriageReturnEnd = i;
|
|
174
229
|
}
|
|
175
230
|
if (i >= len) {
|
|
176
231
|
chunks.push(data.slice(pos));
|
|
@@ -490,7 +545,7 @@ export type GlyphProtocolReportHandler = (supported: boolean) => void;
|
|
|
490
545
|
/**
|
|
491
546
|
* Outcome of the Tern Surface Protocol `hello` probe: the terminal's reply,
|
|
492
547
|
* or null when it answered the DA1 sentinel first (no TSP) or the probe was
|
|
493
|
-
* skipped (`PI_TUI_NATIVE=0`, multiplexers).
|
|
548
|
+
* skipped (`PI_TUI_NATIVE=0`, multiplexers, devenv's reloading shell).
|
|
494
549
|
*/
|
|
495
550
|
export type TspHelloHandler = (hello: TspHello | null) => void;
|
|
496
551
|
|
|
@@ -590,6 +645,11 @@ export interface Terminal {
|
|
|
590
645
|
// so custom Terminals built against older pi-tui versions keep working.
|
|
591
646
|
readonly keyboardEnhancementExitSequence?: string | null;
|
|
592
647
|
|
|
648
|
+
// Re-asserts the main-screen enhanced keyboard mode after alternate-screen
|
|
649
|
+
// exit, or null when none is needed. Optional so custom Terminals built
|
|
650
|
+
// against older pi-tui versions keep working.
|
|
651
|
+
readonly keyboardEnhancementRestoreSequence?: string | null;
|
|
652
|
+
|
|
593
653
|
// Cursor positioning (relative to current position)
|
|
594
654
|
moveBy(lines: number): void; // Move cursor up (negative) or down (positive) by N lines
|
|
595
655
|
|
|
@@ -764,9 +824,11 @@ export interface ProcessTerminalOptions {
|
|
|
764
824
|
conpty?: boolean;
|
|
765
825
|
/**
|
|
766
826
|
* Whether stdin is a native Windows console handle (win32, not WSL), whose
|
|
767
|
-
* console host
|
|
768
|
-
*
|
|
769
|
-
*
|
|
827
|
+
* console host is certain to answer `CSI ? 9001 h` with win32-input-mode key
|
|
828
|
+
* records. When false (WSL), the keyboard fallback requests modifyOtherKeys
|
|
829
|
+
* alongside win32-input-mode, since a WSL pty may not be relayed by a console
|
|
830
|
+
* host. Defaults to `process.platform === "win32"`; only consulted when
|
|
831
|
+
* `conpty` is also true.
|
|
770
832
|
*/
|
|
771
833
|
nativeWindowsConsole?: boolean;
|
|
772
834
|
}
|
|
@@ -783,7 +845,8 @@ export class ProcessTerminal implements Terminal {
|
|
|
783
845
|
#isLoopStalled?: () => boolean;
|
|
784
846
|
#stdoutResizeListener?: () => void;
|
|
785
847
|
#kittyProtocolActive = false;
|
|
786
|
-
|
|
848
|
+
// Kitty flags omp pushed on its own stack frame; meaningful only while kitty is active.
|
|
849
|
+
#kittyFlags = 0;
|
|
787
850
|
#modifyOtherKeysActive = false;
|
|
788
851
|
#modifyOtherKeysTimeout?: Timer;
|
|
789
852
|
// Windows console fallback when kitty is unavailable: key records arrive as
|
|
@@ -793,6 +856,8 @@ export class ProcessTerminal implements Terminal {
|
|
|
793
856
|
// Releases a relayed lone ESC the decoder held for a sequence tail that never came.
|
|
794
857
|
#win32PendingFlushTimer?: Timer;
|
|
795
858
|
#stdinBuffer?: StdinBuffer;
|
|
859
|
+
/** Parsed input path shared by direct VT and win32-input-mode relayed text. */
|
|
860
|
+
#stdinSequenceHandler?: (sequence: string) => void;
|
|
796
861
|
#stdinDataHandler?: (data: string) => void;
|
|
797
862
|
#disconnectHandler?: () => void;
|
|
798
863
|
#stdinEndHandler = () => {
|
|
@@ -878,6 +943,7 @@ export class ProcessTerminal implements Terminal {
|
|
|
878
943
|
#sixelSupportCallbacks: Array<(supported: boolean) => void> = [];
|
|
879
944
|
#tspPending = false;
|
|
880
945
|
#tspResult: TspHello | null | undefined;
|
|
946
|
+
#tspFallbackLogged = false;
|
|
881
947
|
#tspCallbacks: TspHelloHandler[] = [];
|
|
882
948
|
/** Hello deadline inside tmux, where no DA1 sentinel can end the probe. */
|
|
883
949
|
#tspTimeout: NodeJS.Timeout | undefined;
|
|
@@ -889,6 +955,12 @@ export class ProcessTerminal implements Terminal {
|
|
|
889
955
|
/** Resolved DECRQM support per private mode (mode → supported). */
|
|
890
956
|
#privateModeSupport = new Map<number, boolean>();
|
|
891
957
|
#privateModeCallbacks: PrivateModeReportHandler[] = [];
|
|
958
|
+
/**
|
|
959
|
+
* Whether output and input reassert DEC 2004: set once DECRPM confirms it,
|
|
960
|
+
* or at input attach inside a multiplexer that gates paste markers on the
|
|
961
|
+
* pane's own mode without answering DECRQM (tmux <= 3.5a, #15596).
|
|
962
|
+
*/
|
|
963
|
+
#rearmBracketedPaste = false;
|
|
892
964
|
/** Whether DEC 2048 in-band resize notifications are currently enabled. */
|
|
893
965
|
#inBandResizeActive = false;
|
|
894
966
|
/** Reassembly buffer for a DEC 2048 in-band resize report split across stdin reads. */
|
|
@@ -913,14 +985,22 @@ export class ProcessTerminal implements Terminal {
|
|
|
913
985
|
}
|
|
914
986
|
|
|
915
987
|
get kittyEnableSequence(): string | null {
|
|
916
|
-
return this.#kittyProtocolActive ? this.#
|
|
988
|
+
return this.#kittyProtocolActive ? `\x1b[>${this.#kittyFlags}u` : null;
|
|
917
989
|
}
|
|
918
990
|
|
|
919
991
|
get keyboardEnhancementEnterSequence(): string | null {
|
|
920
|
-
if (this.#kittyProtocolActive) return this.#
|
|
992
|
+
if (this.#kittyProtocolActive) return `\x1b[>${this.#kittyFlags}u`;
|
|
921
993
|
return this.#modifyOtherKeysActive ? "\x1b[>4;2m" : null;
|
|
922
994
|
}
|
|
923
995
|
|
|
996
|
+
get keyboardEnhancementRestoreSequence(): string | null {
|
|
997
|
+
// `CSI = flags ; 1 u` replaces the main-screen stack top, which is omp's own
|
|
998
|
+
// frame, so it is a no-op wherever per-screen stacks work. Windows Terminal
|
|
999
|
+
// 1.25–1.27 restore the pre-push flags on `?1049l` (microsoft/terminal#20763),
|
|
1000
|
+
// dropping to legacy keys: Shift+Enter arrives as `\r`, Ctrl+Enter as `\n`.
|
|
1001
|
+
return this.#kittyProtocolActive ? `\x1b[=${this.#kittyFlags};1u` : null;
|
|
1002
|
+
}
|
|
1003
|
+
|
|
924
1004
|
get keyboardEnhancementExitSequence(): string | null {
|
|
925
1005
|
// kitty is a stack push (per-screen), so the matching pop balances alt-screen
|
|
926
1006
|
// entry. xterm modifyOtherKeys is a single global flag with no per-screen
|
|
@@ -1014,13 +1094,13 @@ export class ProcessTerminal implements Terminal {
|
|
|
1014
1094
|
}
|
|
1015
1095
|
|
|
1016
1096
|
get tspExpected(): boolean {
|
|
1017
|
-
// A multiplexer started from Tern can leave `TERM_PROGRAM=tern`
|
|
1018
|
-
// while swallowing APC, even when `PI_TUI_NATIVE=1` forces the probe.
|
|
1097
|
+
// A multiplexer or relay started from Tern can leave `TERM_PROGRAM=tern`
|
|
1098
|
+
// behind while swallowing APC, even when `PI_TUI_NATIVE=1` forces the probe.
|
|
1019
1099
|
return (
|
|
1020
|
-
$env
|
|
1021
|
-
|
|
1100
|
+
isTernPane($env) &&
|
|
1101
|
+
tspPathBlocker($env) === null &&
|
|
1022
1102
|
!isTerminalHeadless() &&
|
|
1023
|
-
this.#
|
|
1103
|
+
this.#tspProbeSkipReason() === null
|
|
1024
1104
|
);
|
|
1025
1105
|
}
|
|
1026
1106
|
|
|
@@ -1048,6 +1128,8 @@ export class ProcessTerminal implements Terminal {
|
|
|
1048
1128
|
// Own stdout: out-of-band writers route through this terminal, and the
|
|
1049
1129
|
// emergency restore finds it on crash.
|
|
1050
1130
|
setActiveTerminal(this);
|
|
1131
|
+
// RGB belongs to this ownership session, including deferred-input prepaint.
|
|
1132
|
+
setTerminalBackgroundColor(undefined);
|
|
1051
1133
|
terminalEverStarted = true;
|
|
1052
1134
|
// Own the blocking write(2) on a pump thread (unix TTYs only). A stale
|
|
1053
1135
|
// prebuilt natives module without the export falls back to direct writes.
|
|
@@ -1128,6 +1210,9 @@ export class ProcessTerminal implements Terminal {
|
|
|
1128
1210
|
|
|
1129
1211
|
// Enable bracketed paste mode - terminal will wrap pastes in \x1b[200~ ... \x1b[201~
|
|
1130
1212
|
this.#safeWrite("\x1b[?2004h");
|
|
1213
|
+
this.#rearmBracketedPaste =
|
|
1214
|
+
terminalMultiplexerSessions($env).some(multiplexer => multiplexer.gatesBracketedPaste) ||
|
|
1215
|
+
classifyTerminalMultiplexerModule($env)?.gatesBracketedPaste === true;
|
|
1131
1216
|
|
|
1132
1217
|
// Force normal cursor-key (DECCKM) and numeric-keypad mode (terminfo
|
|
1133
1218
|
// `rmkx` = "\x1b[?1l\x1b>"). zeta decodes both CSI ("\x1b[A") and SS3
|
|
@@ -1304,7 +1389,30 @@ export class ProcessTerminal implements Terminal {
|
|
|
1304
1389
|
// skip the subparameter tail instead of dropping the whole report.
|
|
1305
1390
|
const inBandResizePattern = /^\x1b\[48;(\d+)(?::[\d:]*)?;(\d+)(?::[\d:]*)?;(\d+)(?::[\d:]*)?;(\d+)(?::[\d:]*)?t$/;
|
|
1306
1391
|
|
|
1307
|
-
this.#
|
|
1392
|
+
this.#stdinSequenceHandler = (sequence: string) => {
|
|
1393
|
+
const decoder = this.#win32InputDecoder;
|
|
1394
|
+
const win32Keys = decoder?.decode(sequence);
|
|
1395
|
+
if (decoder && win32Keys !== undefined) {
|
|
1396
|
+
clearTimeout(this.#win32PendingFlushTimer);
|
|
1397
|
+
this.#win32PendingFlushTimer = undefined;
|
|
1398
|
+
// The record is ESC-led, so it interrupts any direct reply still being
|
|
1399
|
+
// reassembled, just as a raw escape does below. Decoded keys carry no ESC
|
|
1400
|
+
// of their own and would otherwise be appended to that fragment.
|
|
1401
|
+
this.#privateCsiResponseBuffer = "";
|
|
1402
|
+
this.#inBandResizeBuffer = "";
|
|
1403
|
+
this.#osc11ResponseBuffer = "";
|
|
1404
|
+
this.#osc99ResponseBuffer = "";
|
|
1405
|
+
this.#glyphProtocolReplyBuffer = "";
|
|
1406
|
+
this.#tspReplyBuffer = "";
|
|
1407
|
+
for (const key of win32Keys) this.#stdinSequenceHandler?.(key);
|
|
1408
|
+
if (decoder.hasPendingSequence) {
|
|
1409
|
+
this.#win32PendingFlushTimer = setTimeout(
|
|
1410
|
+
() => this.#releaseWin32PendingSequence(),
|
|
1411
|
+
WIN32_RELAYED_ESCAPE_TIMEOUT_MS,
|
|
1412
|
+
);
|
|
1413
|
+
}
|
|
1414
|
+
return;
|
|
1415
|
+
}
|
|
1308
1416
|
// Fast path for plain-text bytes: every escape-probe regex below
|
|
1309
1417
|
// anchors on `^\x1b…`, so a byte that is not ESC can never match. A
|
|
1310
1418
|
// non-bracketed paste of N printable chars arrives as N per-scalar
|
|
@@ -1501,7 +1609,10 @@ export class ProcessTerminal implements Terminal {
|
|
|
1501
1609
|
}
|
|
1502
1610
|
case "tsp": {
|
|
1503
1611
|
// DA1 before any `tsp;r` reply: the terminal doesn't speak TSP.
|
|
1504
|
-
this.#resolveTspSupport(
|
|
1612
|
+
this.#resolveTspSupport(
|
|
1613
|
+
null,
|
|
1614
|
+
"DA1 was answered before a hello reply: the terminal, or a layer in between, dropped the APC query",
|
|
1615
|
+
);
|
|
1505
1616
|
break;
|
|
1506
1617
|
}
|
|
1507
1618
|
}
|
|
@@ -1536,19 +1647,17 @@ export class ProcessTerminal implements Terminal {
|
|
|
1536
1647
|
// ConPTY (native Windows and WSL) drops Shift+letter keypresses
|
|
1537
1648
|
// entirely when flag 4 (report alternate keys) is set. Use flag 1
|
|
1538
1649
|
// (disambiguate only), preserving flag 2 if already active.
|
|
1539
|
-
this.#
|
|
1540
|
-
this.#safeWrite(this.#kittyEnableSeq);
|
|
1650
|
+
this.#kittyFlags = (reportedFlags & 2) !== 0 ? 3 : 1;
|
|
1541
1651
|
} else if ((reportedFlags & 2) !== 0) {
|
|
1542
1652
|
// Preserve event-type reporting already enabled by a parent app.
|
|
1543
1653
|
// Push level-2 to keep its shortcuts reporting consistently.
|
|
1544
|
-
this.#
|
|
1545
|
-
this.#safeWrite(this.#kittyEnableSeq);
|
|
1654
|
+
this.#kittyFlags = 7;
|
|
1546
1655
|
} else {
|
|
1547
1656
|
// Disambiguate escape codes and report base-layout keys for physical
|
|
1548
1657
|
// shortcut matching, without event reporting that caused regression #3259.
|
|
1549
|
-
this.#
|
|
1550
|
-
this.#safeWrite(this.#kittyEnableSeq);
|
|
1658
|
+
this.#kittyFlags = 5;
|
|
1551
1659
|
}
|
|
1660
|
+
this.#safeWrite(`\x1b[>${this.#kittyFlags}u`);
|
|
1552
1661
|
return;
|
|
1553
1662
|
}
|
|
1554
1663
|
|
|
@@ -1635,20 +1744,6 @@ export class ProcessTerminal implements Terminal {
|
|
|
1635
1744
|
return;
|
|
1636
1745
|
}
|
|
1637
1746
|
if (this.#inputHandler) {
|
|
1638
|
-
const decoder = this.#win32InputDecoder;
|
|
1639
|
-
const win32Keys = decoder?.decode(sequence);
|
|
1640
|
-
if (decoder && win32Keys !== undefined) {
|
|
1641
|
-
clearTimeout(this.#win32PendingFlushTimer);
|
|
1642
|
-
this.#win32PendingFlushTimer = undefined;
|
|
1643
|
-
for (const key of win32Keys) this.#inputHandler(key);
|
|
1644
|
-
if (decoder.hasPendingSequence) {
|
|
1645
|
-
this.#win32PendingFlushTimer = setTimeout(
|
|
1646
|
-
() => this.#releaseWin32PendingSequence(),
|
|
1647
|
-
WIN32_RELAYED_ESCAPE_TIMEOUT_MS,
|
|
1648
|
-
);
|
|
1649
|
-
}
|
|
1650
|
-
return;
|
|
1651
|
-
}
|
|
1652
1747
|
// Windows console hosts drop AltGr text under kitty (AltGr+F → `CSI 102;3u`);
|
|
1653
1748
|
// recover it from the active layout before any keybinding sees an Alt chord.
|
|
1654
1749
|
const altGrText =
|
|
@@ -1657,7 +1752,8 @@ export class ProcessTerminal implements Terminal {
|
|
|
1657
1752
|
: undefined;
|
|
1658
1753
|
this.#inputHandler(altGrText ?? sequence);
|
|
1659
1754
|
}
|
|
1660
|
-
}
|
|
1755
|
+
};
|
|
1756
|
+
this.#stdinBuffer.on("data", this.#stdinSequenceHandler);
|
|
1661
1757
|
|
|
1662
1758
|
// Re-wrap paste content with bracketed paste markers for existing editor
|
|
1663
1759
|
// handling. An Enter that shared the paste's stdin read rides along so
|
|
@@ -1673,8 +1769,11 @@ export class ProcessTerminal implements Terminal {
|
|
|
1673
1769
|
|
|
1674
1770
|
// Handler that pipes stdin data through the buffer
|
|
1675
1771
|
this.#stdinDataHandler = (data: string) => {
|
|
1676
|
-
// Recover modes reset by the host before the user's next paste.
|
|
1677
|
-
|
|
1772
|
+
// Recover modes reset by the host before the user's next paste. Not
|
|
1773
|
+
// mid-paste: the host is plainly in 2004 then, and per-key reads would
|
|
1774
|
+
// turn one paste into thousands of writes (#15757).
|
|
1775
|
+
if (data && this.#active && this.#rearmBracketedPaste && !this.#stdinBuffer!.pasting)
|
|
1776
|
+
this.#safeWrite("\x1b[?2004h");
|
|
1678
1777
|
if (this.#win32PasteNormalizer) this.#win32PasteNormalizer.process(data);
|
|
1679
1778
|
else this.#stdinBuffer!.process(data);
|
|
1680
1779
|
};
|
|
@@ -1837,16 +1936,27 @@ export class ProcessTerminal implements Terminal {
|
|
|
1837
1936
|
}
|
|
1838
1937
|
}
|
|
1839
1938
|
|
|
1840
|
-
|
|
1939
|
+
/** Why the `hello` probe does not run, or `null` when it does. */
|
|
1940
|
+
#tspProbeSkipReason(): string | null {
|
|
1841
1941
|
const override = $env.PI_TUI_NATIVE;
|
|
1842
|
-
if (override === "0") return
|
|
1942
|
+
if (override === "0") return "PI_TUI_NATIVE=0";
|
|
1843
1943
|
// tmux makes APC bodies the pane title unless the hosting terminal asked
|
|
1844
1944
|
// for TSP through `@tern-tsp`: no override may force the probe there.
|
|
1845
|
-
if (tmuxMultiplexer.isInside($env))
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
if (
|
|
1849
|
-
|
|
1945
|
+
if (tmuxMultiplexer.isInside($env)) {
|
|
1946
|
+
return tmuxSessionSpeaksTsp($env) ? null : "inside a tmux session without the @tern-tsp option";
|
|
1947
|
+
}
|
|
1948
|
+
if (override === "1") return null;
|
|
1949
|
+
// The reply could never arrive.
|
|
1950
|
+
const blocker = tspPathBlocker($env);
|
|
1951
|
+
if (blocker !== null) return blocker;
|
|
1952
|
+
return isBunTestRuntime() && $env.PI_TUI_TSP_PROBE !== "1" ? "bun test runtime" : null;
|
|
1953
|
+
}
|
|
1954
|
+
|
|
1955
|
+
/** A Tern pane that ends up on the classic renderer says why in the log, once. */
|
|
1956
|
+
#logTspFallback(reason: string): void {
|
|
1957
|
+
if (this.#tspFallbackLogged || !isTernPane($env)) return;
|
|
1958
|
+
this.#tspFallbackLogged = true;
|
|
1959
|
+
logger.debug("TSP: Tern pane keeps the classic renderer", { reason });
|
|
1850
1960
|
}
|
|
1851
1961
|
|
|
1852
1962
|
#queryTspSupport(): void {
|
|
@@ -1854,7 +1964,9 @@ export class ProcessTerminal implements Terminal {
|
|
|
1854
1964
|
this.#tspResult = undefined;
|
|
1855
1965
|
this.#tspReplyBuffer = "";
|
|
1856
1966
|
if (this.#dead) return;
|
|
1857
|
-
|
|
1967
|
+
const skipReason = this.#tspProbeSkipReason();
|
|
1968
|
+
if (skipReason !== null) {
|
|
1969
|
+
this.#logTspFallback(skipReason);
|
|
1858
1970
|
this.#tspResult = null;
|
|
1859
1971
|
for (const cb of this.#tspCallbacks) cb(null);
|
|
1860
1972
|
return;
|
|
@@ -1866,7 +1978,7 @@ export class ProcessTerminal implements Terminal {
|
|
|
1866
1978
|
setTmuxPaneApcTitles(false, $env);
|
|
1867
1979
|
this.#tspTimeout = setTimeout(() => {
|
|
1868
1980
|
this.#tspTimeout = undefined;
|
|
1869
|
-
this.#resolveTspSupport(null);
|
|
1981
|
+
this.#resolveTspSupport(null, `no hello reply through tmux within ${TSP_TMUX_HELLO_TIMEOUT_MS} ms`);
|
|
1870
1982
|
}, TSP_TMUX_HELLO_TIMEOUT_MS);
|
|
1871
1983
|
this.#safeWrite(encodeTspHelloQuery());
|
|
1872
1984
|
return;
|
|
@@ -1894,12 +2006,14 @@ export class ProcessTerminal implements Terminal {
|
|
|
1894
2006
|
this.#inputHandler?.(sequence);
|
|
1895
2007
|
}
|
|
1896
2008
|
|
|
1897
|
-
|
|
2009
|
+
/** `reason` says why a `null` hello means no TSP. */
|
|
2010
|
+
#resolveTspSupport(hello: TspHello | null, reason?: string): void {
|
|
1898
2011
|
if (!this.#tspPending) return;
|
|
1899
2012
|
this.#tspPending = false;
|
|
1900
2013
|
const result = hello !== null && hello.v === TSP_VERSION ? hello : null;
|
|
1901
2014
|
if (hello !== null && result === null) logger.warn("TSP: unsupported protocol version", { v: hello.v });
|
|
1902
2015
|
if (result === null) {
|
|
2016
|
+
this.#logTspFallback(reason ?? `unsupported protocol version ${String(hello?.v)}`);
|
|
1903
2017
|
this.#endTspTmuxPane();
|
|
1904
2018
|
} else {
|
|
1905
2019
|
clearTimeout(this.#tspTimeout);
|
|
@@ -1942,7 +2056,11 @@ export class ProcessTerminal implements Terminal {
|
|
|
1942
2056
|
const max = 16 ** hex.length - 1;
|
|
1943
2057
|
return max > 0 ? value / max : 0;
|
|
1944
2058
|
};
|
|
1945
|
-
const
|
|
2059
|
+
const r = normalize(rHex);
|
|
2060
|
+
const g = normalize(gHex);
|
|
2061
|
+
const b = normalize(bHex);
|
|
2062
|
+
setTerminalBackgroundColor((Math.round(r * 255) << 16) | (Math.round(g * 255) << 8) | Math.round(b * 255));
|
|
2063
|
+
const luminance = 0.299 * r + 0.587 * g + 0.114 * b;
|
|
1946
2064
|
const mode: TerminalAppearance = luminance < 0.5 ? "dark" : "light";
|
|
1947
2065
|
const changed = mode !== this.#appearance;
|
|
1948
2066
|
this.#appearance = mode;
|
|
@@ -1967,10 +2085,11 @@ export class ProcessTerminal implements Terminal {
|
|
|
1967
2085
|
#enableModifyOtherKeysFallback(): void {
|
|
1968
2086
|
if (this.#kittyProtocolActive || this.#modifyOtherKeysActive || this.#win32InputDecoder) return;
|
|
1969
2087
|
const ssh = isSshSession();
|
|
1970
|
-
if (this.#conpty &&
|
|
2088
|
+
if (this.#conpty && !ssh) {
|
|
1971
2089
|
// The Windows console host ignores modifyOtherKeys and folds Shift+Enter
|
|
1972
|
-
// into a bare CR. win32-input-mode is answered by the
|
|
1973
|
-
// serving this process, so it works under every local ConPTY
|
|
2090
|
+
// into a bare CR (Ctrl+Enter into LF). win32-input-mode is answered by the
|
|
2091
|
+
// console host serving this process, so it works under every local ConPTY
|
|
2092
|
+
// terminal, and under WSL through the `wsl.exe` relay's console host too.
|
|
1974
2093
|
// Under sshd the console host is fed the remote terminal's VT stream,
|
|
1975
2094
|
// and the mode splits arrow keys into Escape plus literal text (#14034).
|
|
1976
2095
|
this.#safeWrite("\x1b[?9001h");
|
|
@@ -1981,7 +2100,10 @@ export class ProcessTerminal implements Terminal {
|
|
|
1981
2100
|
data => this.#stdinBuffer?.process(data),
|
|
1982
2101
|
() => this.#releaseWin32PendingSequence(),
|
|
1983
2102
|
);
|
|
1984
|
-
|
|
2103
|
+
// A WSL pty is not always relayed by a console host (tmux, VS Code
|
|
2104
|
+
// Remote-WSL, WSLg terminals). Those ignore 9001 and keep sending plain
|
|
2105
|
+
// VT, which the decoder passes through, so also ask for modifyOtherKeys.
|
|
2106
|
+
if (this.#nativeWindowsConsole) return;
|
|
1985
2107
|
}
|
|
1986
2108
|
// A remote terminal with no identifying env may not understand the request.
|
|
1987
2109
|
if (ssh && (TERMINAL.id === "base" || TERMINAL.id === "trueColor")) return;
|
|
@@ -1994,7 +2116,7 @@ export class ProcessTerminal implements Terminal {
|
|
|
1994
2116
|
clearTimeout(this.#win32PendingFlushTimer);
|
|
1995
2117
|
this.#win32PendingFlushTimer = undefined;
|
|
1996
2118
|
const pending = this.#win32InputDecoder?.flushPendingSequence() ?? [];
|
|
1997
|
-
for (const key of pending) this.#
|
|
2119
|
+
for (const key of pending) this.#stdinSequenceHandler?.(key);
|
|
1998
2120
|
}
|
|
1999
2121
|
|
|
2000
2122
|
#disableWin32InputMode(): void {
|
|
@@ -2075,6 +2197,7 @@ export class ProcessTerminal implements Terminal {
|
|
|
2075
2197
|
// only true here after an explicit DECRPM reply (the DA1-sentinel
|
|
2076
2198
|
// fallback resolves unsupported).
|
|
2077
2199
|
if (mode === 2004 && supported) {
|
|
2200
|
+
this.#rearmBracketedPaste = true;
|
|
2078
2201
|
this.#stdinBuffer?.setRawPasteStallProbe(this.#isLoopStalled);
|
|
2079
2202
|
}
|
|
2080
2203
|
}
|
|
@@ -2209,11 +2332,13 @@ export class ProcessTerminal implements Terminal {
|
|
|
2209
2332
|
// Suppress observer/timer callbacks before any teardown can yield or throw.
|
|
2210
2333
|
this.#active = false;
|
|
2211
2334
|
this.#inputDeferred = false;
|
|
2212
|
-
if (this.#headless) return;
|
|
2213
2335
|
// Release stdout ownership (out-of-band writers and emergency cleanup)
|
|
2336
|
+
// before headless/teardown exits, without disturbing another owner's RGB.
|
|
2214
2337
|
if (getActiveTerminal() === this) {
|
|
2215
2338
|
setActiveTerminal(null);
|
|
2339
|
+
setTerminalBackgroundColor(undefined);
|
|
2216
2340
|
}
|
|
2341
|
+
if (this.#headless) return;
|
|
2217
2342
|
|
|
2218
2343
|
// Release terminal ownership of fd 2 first so external programs,
|
|
2219
2344
|
// suspend, and shutdown see the real stderr even if a later teardown
|
|
@@ -2302,6 +2427,7 @@ export class ProcessTerminal implements Terminal {
|
|
|
2302
2427
|
this.#da1SentinelOwners.length = 0;
|
|
2303
2428
|
this.#privateModeCallbacks = [];
|
|
2304
2429
|
this.#privateModeSupport.clear();
|
|
2430
|
+
this.#rearmBracketedPaste = false;
|
|
2305
2431
|
this.#xtermScrollToBottomRestoreModes.clear();
|
|
2306
2432
|
this.#reportedColumns = undefined;
|
|
2307
2433
|
this.#reportedRows = undefined;
|
|
@@ -2328,6 +2454,7 @@ export class ProcessTerminal implements Terminal {
|
|
|
2328
2454
|
this.#stdinBuffer.destroy();
|
|
2329
2455
|
this.#stdinBuffer = undefined;
|
|
2330
2456
|
}
|
|
2457
|
+
this.#stdinSequenceHandler = undefined;
|
|
2331
2458
|
|
|
2332
2459
|
// Remove event handlers
|
|
2333
2460
|
if (this.#stdinDataHandler) {
|
|
@@ -2413,7 +2540,7 @@ export class ProcessTerminal implements Terminal {
|
|
|
2413
2540
|
write(data: string): void {
|
|
2414
2541
|
// Hosts can reset bracketed paste (e.g. iTerm2's Terminal State toggle).
|
|
2415
2542
|
// Reassert it with existing output, never on a timer while idle.
|
|
2416
|
-
if (data && this.#active && this.#
|
|
2543
|
+
if (data && this.#active && this.#rearmBracketedPaste) data = `\x1b[?2004h${data}`;
|
|
2417
2544
|
this.#safeWrite(data);
|
|
2418
2545
|
if (this.#writeLogPath) {
|
|
2419
2546
|
try {
|
package/src/theme/symbols.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ export type SymbolMap = Record<SymbolKey, string>;
|
|
|
9
9
|
* resolves through `Theme.cmd` to either a dedicated `cmd.*` symbol or an
|
|
10
10
|
* existing `icon.*` symbol shared with the rest of the UI.
|
|
11
11
|
*/
|
|
12
|
-
export type SlashCommandIconName = "action" | "prompt" | "extension" | "settings" | "gear" | "shield" | "wave" | "compass" | "inbox" | "swap" | "expand" | "computer" | "eye" | "todo" | "stats" | "news" | "keyboard" | "export" | "clipboard" | "share" | "broadcast" | "globe" | "copy" | "plus" | "restart" | "eraser" | "trash" | "compress" | "vibrate" | "handoff" | "history" | "question" | "rocket" | "stethoscope" | "redo" | "bug" | "memory" | "pencil" | "folderMove" | "folderPlus" | "folderMinus" | "hammer" | "power" | "cart" | "model" | "plan" | "prewalk" | "goal" | "pause" | "loop" | "session" | "jobs" | "gauge" | "context" | "agents" | "branch" | "tree" | "signIn" | "signOut" | "advisor" | "host" | "package" | "fast" | "voice" | "tools" | "rule" | "skill" | "mcp" | "pin";
|
|
12
|
+
export type SlashCommandIconName = "action" | "prompt" | "extension" | "settings" | "gear" | "shield" | "wave" | "compass" | "inbox" | "swap" | "expand" | "computer" | "eye" | "todo" | "stats" | "news" | "keyboard" | "export" | "clipboard" | "share" | "broadcast" | "globe" | "copy" | "plus" | "restart" | "eraser" | "trash" | "compress" | "vibrate" | "handoff" | "history" | "question" | "rocket" | "stethoscope" | "redo" | "bug" | "memory" | "pencil" | "folderMove" | "folderPlus" | "folderMinus" | "hammer" | "power" | "cart" | "model" | "plan" | "prewalk" | "goal" | "pause" | "loop" | "session" | "jobs" | "gauge" | "context" | "ghost" | "agents" | "branch" | "tree" | "signIn" | "signOut" | "advisor" | "host" | "package" | "fast" | "voice" | "tools" | "rule" | "skill" | "mcp" | "pin";
|
|
13
13
|
export declare const SYMBOL_PRESETS: Record<SymbolPreset, SymbolMap>;
|
|
14
14
|
export type SpinnerType = "status" | "activity";
|
|
15
15
|
export declare const SPINNER_FRAMES: Record<SymbolPreset, Record<SpinnerType, string[]>>;
|
package/src/theme/symbols.ts
CHANGED
package/src/theme/theme-class.ts
CHANGED
|
@@ -729,6 +729,7 @@ export class Theme {
|
|
|
729
729
|
jobs: this.#symbols["icon.job"],
|
|
730
730
|
gauge: this.#symbols["icon.throughput"],
|
|
731
731
|
context: this.#symbols["icon.context"],
|
|
732
|
+
ghost: this.#symbols["icon.ghost"],
|
|
732
733
|
agents: this.#symbols["icon.agents"],
|
|
733
734
|
branch: this.#symbols["icon.branch"],
|
|
734
735
|
tree: this.#symbols["icon.worktree"],
|
package/src/theme/theme.d.ts
CHANGED
|
@@ -7,6 +7,17 @@ export { isValidThemeColor, type ThemeBg, type ThemeColor } from "./schema";
|
|
|
7
7
|
export { getAvailableSymbolPresets, isValidSymbolPreset, type SpinnerType, type SymbolKey, type SymbolPreset, } from "./symbols";
|
|
8
8
|
export { Theme } from "./theme-class";
|
|
9
9
|
export { createHighlightStream, getEditorTheme, getMarkdownTheme, getMarkdownThemeWithLinkTargets, getSelectListTheme, getSettingsListTheme, getSymbolTheme, highlightCode, setMarkdownMermaidRendering, warmHighlighter, } from "./tui-adapters";
|
|
10
|
+
/**
|
|
11
|
+
* Subscribe to native fallback appearance changes independently of theme changes.
|
|
12
|
+
* Consumers must re-check the effective background: COLORFGBG can override it.
|
|
13
|
+
*/
|
|
14
|
+
export declare function onTerminalBackgroundFallbackChange(callback: () => void): () => void;
|
|
15
|
+
/**
|
|
16
|
+
* Detect appearance from trusted OSC 11 reports, then COLORFGBG, then native
|
|
17
|
+
* macOS appearance only on known-broken multiplexer background-query paths.
|
|
18
|
+
* Defaults to dark when none of those signals is available.
|
|
19
|
+
*/
|
|
20
|
+
export declare function detectTerminalBackground(): "dark" | "light";
|
|
10
21
|
/** Active theme; imports remain live across initialization, previews, and theme switches. */
|
|
11
22
|
export declare var theme: Theme;
|
|
12
23
|
type ThemeBinding = (value: Theme) => void;
|