@linxiraos/pi-tui 1.1.27 → 1.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/CHANGELOG.md +47 -463
- package/README.md +7 -5
- package/dist/types/app-keybindings.d.ts +1 -1
- package/dist/types/apps/cleanse-board.d.ts +1 -1
- package/dist/types/apps/debug/log-viewer.d.ts +4 -0
- package/dist/types/apps/debug/raw-sse-buffer.d.ts +2 -0
- package/dist/types/apps/git/git-tui.d.ts +2 -2
- package/dist/types/apps/git/help.d.ts +3 -1
- package/dist/types/apps/if-bench-board.d.ts +2 -2
- package/dist/types/apps/setup-model-picker.d.ts +1 -1
- package/dist/types/autocomplete.d.ts +5 -2
- package/dist/types/charts/chart-plan.d.ts +674 -0
- package/dist/types/charts/chart-svg.d.ts +59 -0
- package/dist/types/charts/table-data.d.ts +175 -0
- package/dist/types/chat/assistant-message.d.ts +5 -0
- package/dist/types/chat/chat-transcript-builder.d.ts +2 -0
- package/dist/types/chat/compaction-summary-message.d.ts +1 -0
- package/dist/types/chat/execution-shared.d.ts +6 -5
- package/dist/types/chat/fence-figure.d.ts +42 -0
- package/dist/types/chat/figure-markdown.d.ts +42 -0
- package/dist/types/chat/messages.d.ts +1 -1
- package/dist/types/chat/svg-figure.d.ts +33 -0
- package/dist/types/chat/svg-source.d.ts +49 -0
- package/dist/types/chat/table-chart.d.ts +89 -0
- package/dist/types/chat/tool-execution.d.ts +2 -0
- package/dist/types/chat/user-message.d.ts +15 -2
- package/dist/types/chrome/shared.d.ts +9 -0
- package/dist/types/chrome/transcript-container.d.ts +6 -0
- package/dist/types/components/box.d.ts +1 -0
- package/dist/types/components/disclosure.d.ts +1 -0
- package/dist/types/components/editor.d.ts +15 -4
- package/dist/types/components/form.d.ts +1 -0
- package/dist/types/components/image.d.ts +12 -44
- package/dist/types/components/key-value-list.d.ts +1 -0
- package/dist/types/components/layout/row.d.ts +1 -0
- package/dist/types/components/layout/stack.d.ts +1 -0
- package/dist/types/components/markdown.d.ts +36 -3
- package/dist/types/components/scroll-view.d.ts +1 -0
- package/dist/types/components/section.d.ts +1 -0
- package/dist/types/components/table.d.ts +1 -0
- package/dist/types/components/text.d.ts +1 -0
- package/dist/types/components/tree-view.d.ts +7 -0
- package/dist/types/components/truncated-text.d.ts +1 -0
- package/dist/types/fuzzy.d.ts +29 -1
- package/dist/types/key-hint-format.d.ts +1 -1
- package/dist/types/keybinding-matchers.d.ts +3 -0
- package/dist/types/keybindings.d.ts +1 -1
- package/dist/types/kitty-graphics.d.ts +2 -2
- package/dist/types/loop-watchdog.d.ts +8 -7
- package/dist/types/multiplexers/cmux.d.ts +16 -0
- package/dist/types/multiplexers/herdr.d.ts +26 -0
- package/dist/types/multiplexers/notify.d.ts +13 -0
- package/dist/types/multiplexers/orca.d.ts +12 -0
- package/dist/types/multiplexers/screen.d.ts +9 -0
- package/dist/types/multiplexers/tmux.d.ts +29 -0
- package/dist/types/multiplexers/types.d.ts +100 -0
- package/dist/types/multiplexers/wmux.d.ts +12 -0
- package/dist/types/multiplexers/zellij.d.ts +12 -0
- package/dist/types/native/backend.d.ts +3 -2
- package/dist/types/native/blobs.d.ts +7 -3
- package/dist/types/native/encode.d.ts +3 -4
- package/dist/types/native/overlay.d.ts +5 -0
- package/dist/types/overlays/agent-hub-projection.d.ts +6 -5
- package/dist/types/overlays/agent-hub-types.d.ts +9 -0
- package/dist/types/overlays/agent-hub.d.ts +2 -0
- package/dist/types/overlays/agent-transcript-viewer.d.ts +11 -2
- package/dist/types/overlays/agents-hub.d.ts +9 -0
- package/dist/types/overlays/annotation-overlay.d.ts +19 -2
- package/dist/types/overlays/annotation-types.d.ts +2 -0
- package/dist/types/overlays/ask-dialog.d.ts +5 -2
- package/dist/types/overlays/history-search.d.ts +10 -0
- package/dist/types/overlays/hook-selector.d.ts +9 -4
- package/dist/types/overlays/jobs-panel.d.ts +1 -7
- package/dist/types/overlays/model-browser.d.ts +54 -7
- package/dist/types/overlays/model-hub.d.ts +23 -0
- package/dist/types/overlays/oauth-selector.d.ts +0 -1
- package/dist/types/overlays/plan-review-overlay.d.ts +3 -2
- package/dist/types/overlays/session-observer-registry.d.ts +2 -0
- package/dist/types/overlays/session-selector.d.ts +6 -2
- package/dist/types/overlays/settings-defs.d.ts +1 -0
- package/dist/types/overlays/usage-dashboard.d.ts +8 -4
- package/dist/types/overlays/usage-display.d.ts +3 -1
- package/dist/types/prompt/composer.d.ts +1 -1
- package/dist/types/prompt/custom-editor.d.ts +11 -8
- package/dist/types/prompt/image-source.d.ts +2 -1
- package/dist/types/prompt/interactive-context-helpers.d.ts +4 -1
- package/dist/types/prompt/model-mention-autocomplete.d.ts +1 -1
- package/dist/types/prompt/welcome.d.ts +6 -1
- package/dist/types/render/composite.d.ts +2 -0
- package/dist/types/render/output-pane.d.ts +1 -0
- package/dist/types/render/render-utils.d.ts +1 -0
- package/dist/types/render/tool-card.d.ts +6 -0
- package/dist/types/render/width-aware-text.d.ts +2 -0
- package/dist/types/setup/scenes/types.d.ts +1 -0
- package/dist/types/status-line/component.d.ts +3 -0
- package/dist/types/status-line/host.d.ts +4 -1
- package/dist/types/status-line/segments.d.ts +2 -0
- package/dist/types/status-line/types.d.ts +14 -3
- package/dist/types/terminal-capabilities.d.ts +27 -35
- package/dist/types/terminal-multiplexer.d.ts +127 -14
- package/dist/types/terminal.d.ts +22 -6
- package/dist/types/theme/active-symbols.d.ts +1 -1
- package/dist/types/theme/schema.d.ts +11 -2
- package/dist/types/theme/symbols.d.ts +1 -1
- package/dist/types/theme/theme-class.d.ts +7 -0
- package/dist/types/theme/theme.d.ts +1 -1
- package/dist/types/theme/tui-adapters.d.ts +3 -0
- package/dist/types/tmux.d.ts +11 -4
- package/dist/types/tools/daemon.d.ts +1 -1
- package/dist/types/tools/eval.d.ts +22 -3
- package/dist/types/tools/glob.d.ts +4 -0
- package/dist/types/tools/native-view.d.ts +47 -0
- package/dist/types/tools/output-meta.d.ts +11 -1
- package/dist/types/tools/renderer.d.ts +37 -2
- package/dist/types/tools/streaming-output.d.ts +20 -1
- package/dist/types/tools/task.d.ts +0 -4
- package/dist/types/tools/write.d.ts +9 -2
- package/dist/types/tools/yield.d.ts +22 -0
- package/dist/types/tui.d.ts +31 -0
- package/dist/types/utils.d.ts +6 -0
- package/dist/types/vim.d.ts +19 -5
- package/package.json +13 -9
- package/src/app-keybindings.d.ts +1 -1
- package/src/app-keybindings.ts +1 -1
- package/src/apps/autoresearch-dashboard.ts +7 -3
- package/src/apps/cleanse-board.ts +2 -2
- package/src/apps/cleanse-picker.ts +1 -1
- package/src/apps/debug/log-viewer.ts +188 -72
- package/src/apps/debug/raw-sse-buffer.ts +8 -0
- package/src/apps/debug/raw-sse.ts +39 -5
- package/src/apps/git/diff-pane.ts +26 -5
- package/src/apps/git/git-tui.ts +2 -2
- package/src/apps/git/help.ts +29 -20
- package/src/apps/if-bench-board.ts +2 -2
- package/src/apps/ps-top.ts +2 -2
- package/src/apps/setup-model-picker.ts +1 -1
- package/src/autocomplete.d.ts +5 -2
- package/src/autocomplete.ts +67 -43
- package/src/charts/chart-plan.d.ts +674 -0
- package/src/charts/chart-plan.ts +5081 -0
- package/src/charts/chart-svg.d.ts +59 -0
- package/src/charts/chart-svg.ts +4657 -0
- package/src/charts/table-data.d.ts +175 -0
- package/src/charts/table-data.ts +1478 -0
- package/src/chat/advisor-message.ts +11 -0
- package/src/chat/assistant-message.d.ts +5 -0
- package/src/chat/assistant-message.ts +219 -50
- package/src/chat/chat-transcript-builder.d.ts +2 -0
- package/src/chat/chat-transcript-builder.ts +5 -3
- package/src/chat/compaction-summary-message.d.ts +1 -0
- package/src/chat/compaction-summary-message.ts +8 -0
- package/src/chat/execution-shared.d.ts +6 -5
- package/src/chat/execution-shared.ts +42 -25
- package/src/chat/fence-figure.d.ts +42 -0
- package/src/chat/fence-figure.ts +80 -0
- package/src/chat/figure-markdown.d.ts +42 -0
- package/src/chat/figure-markdown.ts +174 -0
- package/src/chat/messages.d.ts +1 -1
- package/src/chat/messages.ts +1 -1
- package/src/chat/read-tool-group.ts +6 -4
- package/src/chat/skill-message.ts +6 -0
- package/src/chat/svg-figure.d.ts +33 -0
- package/src/chat/svg-figure.ts +399 -0
- package/src/chat/svg-source.d.ts +49 -0
- package/src/chat/svg-source.ts +188 -0
- package/src/chat/table-chart.d.ts +89 -0
- package/src/chat/table-chart.ts +294 -0
- package/src/chat/tool-execution.d.ts +2 -0
- package/src/chat/tool-execution.ts +80 -6
- package/src/chat/user-message.d.ts +15 -2
- package/src/chat/user-message.ts +55 -3
- package/src/chrome/shared.ts +53 -0
- package/src/chrome/transcript-container.d.ts +6 -0
- package/src/chrome/transcript-container.ts +93 -13
- package/src/components/box.d.ts +1 -0
- package/src/components/box.ts +5 -0
- package/src/components/disclosure.d.ts +1 -0
- package/src/components/disclosure.ts +8 -0
- package/src/components/editor.d.ts +15 -4
- package/src/components/editor.ts +294 -46
- package/src/components/form.d.ts +1 -0
- package/src/components/form.ts +8 -0
- package/src/components/image.d.ts +12 -44
- package/src/components/image.ts +47 -151
- package/src/components/key-value-list.d.ts +1 -0
- package/src/components/key-value-list.ts +4 -0
- package/src/components/layout/row.d.ts +1 -0
- package/src/components/layout/row.ts +7 -0
- package/src/components/layout/stack.d.ts +1 -0
- package/src/components/layout/stack.ts +7 -0
- package/src/components/markdown.d.ts +36 -3
- package/src/components/markdown.ts +433 -318
- package/src/components/scroll-view.d.ts +1 -0
- package/src/components/scroll-view.ts +5 -0
- package/src/components/section.d.ts +1 -0
- package/src/components/section.ts +5 -0
- package/src/components/settings-list.ts +2 -0
- package/src/components/table.d.ts +1 -0
- package/src/components/table.ts +4 -0
- package/src/components/text.d.ts +1 -0
- package/src/components/text.ts +7 -0
- package/src/components/tree-view.d.ts +7 -0
- package/src/components/tree-view.ts +38 -8
- package/src/components/truncated-text.d.ts +1 -0
- package/src/components/truncated-text.ts +5 -0
- package/src/desktop-notify.ts +1 -1
- package/src/fuzzy.d.ts +29 -1
- package/src/fuzzy.ts +115 -30
- package/src/key-hint-format.d.ts +1 -1
- package/src/key-hint-format.ts +1 -1
- package/src/keybinding-matchers.d.ts +3 -0
- package/src/keybinding-matchers.ts +6 -2
- package/src/keybindings.d.ts +1 -1
- package/src/keybindings.ts +1 -1
- package/src/kitty-graphics.d.ts +2 -2
- package/src/kitty-graphics.ts +4 -4
- package/src/loop-watchdog.d.ts +8 -7
- package/src/loop-watchdog.ts +18 -13
- package/src/multiplexers/cmux.d.ts +16 -0
- package/src/multiplexers/cmux.ts +29 -0
- package/src/multiplexers/herdr.d.ts +26 -0
- package/src/multiplexers/herdr.ts +64 -0
- package/src/multiplexers/notify.d.ts +13 -0
- package/src/multiplexers/notify.ts +24 -0
- package/src/multiplexers/orca.d.ts +12 -0
- package/src/multiplexers/orca.ts +16 -0
- package/src/multiplexers/screen.d.ts +9 -0
- package/src/multiplexers/screen.ts +15 -0
- package/src/multiplexers/tmux.d.ts +29 -0
- package/src/multiplexers/tmux.ts +40 -0
- package/src/multiplexers/types.d.ts +100 -0
- package/src/multiplexers/types.ts +110 -0
- package/src/multiplexers/wmux.d.ts +12 -0
- package/src/multiplexers/wmux.ts +16 -0
- package/src/multiplexers/zellij.d.ts +12 -0
- package/src/multiplexers/zellij.ts +24 -0
- package/src/native/backend.d.ts +3 -2
- package/src/native/backend.ts +174 -18
- package/src/native/blobs.d.ts +7 -3
- package/src/native/blobs.ts +47 -12
- package/src/native/encode.d.ts +3 -4
- package/src/native/encode.ts +58 -31
- package/src/native/overlay.d.ts +5 -0
- package/src/native/overlay.ts +13 -0
- package/src/native/reconcile.ts +4 -0
- package/src/overlays/agent-hub-projection.d.ts +6 -5
- package/src/overlays/agent-hub-projection.ts +124 -13
- package/src/overlays/agent-hub-types.d.ts +9 -0
- package/src/overlays/agent-hub-types.ts +6 -0
- package/src/overlays/agent-hub.ts +44 -29
- package/src/overlays/agent-transcript-viewer.ts +91 -23
- package/src/overlays/agents-hub.ts +28 -8
- package/src/overlays/annotation-overlay.ts +293 -90
- package/src/overlays/annotation-types.ts +2 -0
- package/src/overlays/ask-dialog.ts +7 -10
- package/src/overlays/btw-history-panel.ts +12 -1
- package/src/overlays/copy-selector.ts +30 -8
- package/src/overlays/extensions/state-manager.ts +52 -12
- package/src/overlays/history-search.ts +100 -7
- package/src/overlays/hook-selector.ts +12 -5
- package/src/overlays/jobs-panel.ts +28 -46
- package/src/overlays/model-browser.ts +341 -110
- package/src/overlays/model-hub.ts +300 -49
- package/src/overlays/move-overlay.ts +28 -14
- package/src/overlays/oauth-selector.ts +54 -70
- package/src/overlays/plan-review-overlay.ts +194 -70
- package/src/overlays/plugin-settings.ts +3 -3
- package/src/overlays/report-panel.ts +2 -2
- package/src/overlays/rewind-selector.ts +86 -17
- package/src/overlays/session-observer-registry.d.ts +2 -0
- package/src/overlays/session-observer-registry.ts +10 -2
- package/src/overlays/session-selector.ts +64 -26
- package/src/overlays/settings-defs.ts +4 -3
- package/src/overlays/settings-selector.ts +33 -8
- package/src/overlays/snapcompact-shape-preview.ts +2 -3
- package/src/overlays/stats-notice.ts +1 -1
- package/src/overlays/tree-selector.ts +41 -20
- package/src/overlays/usage-dashboard.ts +100 -50
- package/src/overlays/usage-display.ts +10 -4
- package/src/prompt/attachment-chips.ts +15 -11
- package/src/prompt/composer-attachments.ts +6 -4
- package/src/prompt/composer.ts +4 -15
- package/src/prompt/custom-editor.ts +54 -26
- package/src/prompt/image-source.ts +2 -1
- package/src/prompt/interactive-context-helpers.ts +30 -18
- package/src/prompt/macos-spelling.ts +7 -0
- package/src/prompt/magic-keywords.ts +11 -2
- package/src/prompt/model-mention-autocomplete.ts +14 -14
- package/src/prompt/tips.txt +6 -5
- package/src/prompt/welcome.ts +55 -12
- package/src/prompt/word-completion.ts +38 -5
- package/src/render/composite.d.ts +2 -0
- package/src/render/composite.ts +64 -0
- package/src/render/output-pane.d.ts +1 -0
- package/src/render/output-pane.ts +6 -0
- package/src/render/render-utils.d.ts +1 -0
- package/src/render/render-utils.ts +11 -0
- package/src/render/tool-card.d.ts +6 -0
- package/src/render/tool-card.ts +15 -0
- package/src/render/width-aware-text.d.ts +2 -0
- package/src/render/width-aware-text.ts +5 -0
- package/src/setup/scenes/model.ts +2 -1
- package/src/setup/scenes/types.ts +1 -0
- package/src/status-line/component.ts +292 -78
- package/src/status-line/host.ts +4 -1
- package/src/status-line/segments.ts +40 -22
- package/src/status-line/types.ts +15 -3
- package/src/terminal-capabilities.d.ts +27 -35
- package/src/terminal-capabilities.ts +75 -189
- package/src/terminal-multiplexer.d.ts +127 -14
- package/src/terminal-multiplexer.ts +112 -33
- package/src/terminal.d.ts +22 -6
- package/src/terminal.ts +139 -37
- package/src/theme/active-symbols.d.ts +1 -1
- package/src/theme/active-symbols.ts +1 -1
- package/src/theme/schema-validation.ts +1 -0
- package/src/theme/schema.d.ts +11 -2
- package/src/theme/schema.ts +14 -1
- package/src/theme/session-color.ts +1 -7
- package/src/theme/symbols.d.ts +1 -1
- package/src/theme/symbols.ts +6 -1
- package/src/theme/theme-class.d.ts +7 -0
- package/src/theme/theme-class.ts +26 -1
- package/src/theme/theme-schema.json +5 -1
- package/src/theme/theme.d.ts +1 -1
- package/src/theme/theme.ts +8 -4
- package/src/theme/tui-adapters.d.ts +3 -0
- package/src/theme/tui-adapters.ts +68 -0
- package/src/tmux.d.ts +11 -4
- package/src/tmux.ts +47 -11
- package/src/tools/ast-edit.ts +59 -35
- package/src/tools/ast-grep.ts +35 -16
- package/src/tools/bash.ts +92 -42
- package/src/tools/daemon.d.ts +1 -1
- package/src/tools/daemon.ts +1 -1
- package/src/tools/default-renderer.ts +35 -19
- package/src/tools/edit.ts +142 -120
- package/src/tools/eval-format/index.ts +28 -2
- package/src/tools/eval-format/javascript.ts +7 -5
- package/src/tools/eval.d.ts +22 -3
- package/src/tools/eval.ts +364 -208
- package/src/tools/fetch.ts +5 -6
- package/src/tools/glob.d.ts +4 -0
- package/src/tools/glob.ts +33 -65
- package/src/tools/grep.ts +81 -48
- package/src/tools/index.ts +2 -0
- package/src/tools/lsp.ts +81 -59
- package/src/tools/mcp.ts +111 -68
- package/src/tools/native-view.d.ts +47 -0
- package/src/tools/native-view.ts +102 -1
- package/src/tools/output-meta.d.ts +11 -1
- package/src/tools/output-meta.ts +23 -3
- package/src/tools/read.ts +7 -5
- package/src/tools/renderer.d.ts +37 -2
- package/src/tools/renderer.ts +42 -2
- package/src/tools/streaming-output.d.ts +20 -1
- package/src/tools/streaming-output.ts +149 -50
- package/src/tools/task.d.ts +0 -4
- package/src/tools/task.ts +112 -59
- package/src/tools/todo.ts +119 -30
- package/src/tools/wait.ts +34 -24
- package/src/tools/web-search.ts +1 -1
- package/src/tools/write.d.ts +9 -2
- package/src/tools/write.ts +147 -95
- package/src/tools/yield.d.ts +22 -0
- package/src/tools/yield.ts +225 -0
- package/src/tui.d.ts +31 -0
- package/src/tui.ts +309 -160
- package/src/utils.d.ts +6 -0
- package/src/utils.ts +19 -0
- package/src/vim.d.ts +19 -5
- package/src/vim.ts +333 -18
- package/src/windows-input-mode.ts +8 -0
|
@@ -1,45 +1,124 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
1
|
+
import { cmuxMultiplexer } from "./multiplexers/cmux";
|
|
2
|
+
import { herdrMultiplexer } from "./multiplexers/herdr";
|
|
3
|
+
import { orcaMultiplexer } from "./multiplexers/orca";
|
|
4
|
+
import { screenMultiplexer } from "./multiplexers/screen";
|
|
5
|
+
import { tmuxMultiplexer } from "./multiplexers/tmux";
|
|
6
|
+
import type { TerminalMultiplexerModule, TerminalMultiplexerNotificationRequest } from "./multiplexers/types";
|
|
7
|
+
import { wmuxMultiplexer } from "./multiplexers/wmux";
|
|
8
|
+
import { zellijMultiplexer } from "./multiplexers/zellij";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Every multiplexer omp recognizes, in classification order within each
|
|
12
|
+
* precedence tier. Adding a multiplexer means adding its module here.
|
|
13
|
+
*/
|
|
14
|
+
const registry = [
|
|
15
|
+
herdrMultiplexer,
|
|
16
|
+
tmuxMultiplexer,
|
|
17
|
+
screenMultiplexer,
|
|
18
|
+
zellijMultiplexer,
|
|
19
|
+
cmuxMultiplexer,
|
|
20
|
+
wmuxMultiplexer,
|
|
21
|
+
orcaMultiplexer,
|
|
22
|
+
] as const satisfies readonly TerminalMultiplexerModule[];
|
|
23
|
+
|
|
24
|
+
/** Terminal multiplexers omp recognizes. */
|
|
25
|
+
export type TerminalMultiplexer = (typeof registry)[number]["id"];
|
|
26
|
+
|
|
27
|
+
const TERMINAL_MULTIPLEXERS: readonly TerminalMultiplexerModule<TerminalMultiplexer>[] = registry;
|
|
28
|
+
const MULTIPLEXERS_BY_ID = Object.fromEntries(
|
|
29
|
+
TERMINAL_MULTIPLEXERS.map(multiplexer => [multiplexer.id, multiplexer]),
|
|
30
|
+
) as Record<TerminalMultiplexer, TerminalMultiplexerModule<TerminalMultiplexer>>;
|
|
31
|
+
const SESSION_TIER = TERMINAL_MULTIPLEXERS.filter(multiplexer => multiplexer.precedence === "session");
|
|
32
|
+
const OUTER_APP_TIER = TERMINAL_MULTIPLEXERS.filter(multiplexer => multiplexer.precedence === "outerApp");
|
|
33
|
+
const NOTIFICATION_TIER_ORDER = ["pane", "surface", "inBand"] as const;
|
|
34
|
+
// Innermost target first: a pane inside a surface must receive its own signal
|
|
35
|
+
// before the containing surface, followed by in-band rewrites. Registry order
|
|
36
|
+
// breaks ties within a tier (sort is stable).
|
|
37
|
+
const NOTIFIERS = TERMINAL_MULTIPLEXERS.flatMap(multiplexer =>
|
|
38
|
+
multiplexer.notifier ? [{ multiplexer, notifier: multiplexer.notifier }] : [],
|
|
39
|
+
).sort((a, b) => NOTIFICATION_TIER_ORDER.indexOf(a.notifier.tier) - NOTIFICATION_TIER_ORDER.indexOf(b.notifier.tier));
|
|
40
|
+
|
|
41
|
+
/** Every environment variable multiplexer classification reads, including the TERM fallback. */
|
|
42
|
+
export const TERMINAL_MULTIPLEXER_ENV_KEYS: readonly string[] = [
|
|
43
|
+
...TERMINAL_MULTIPLEXERS.flatMap(multiplexer => multiplexer.sessionEnvKeys),
|
|
44
|
+
"TERM",
|
|
45
|
+
];
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Whether an explicit session marker identifies the current provider.
|
|
49
|
+
*
|
|
50
|
+
* TERM is intentionally excluded: it is a classification fallback, not proof
|
|
51
|
+
* that a particular multiplexer session owns the current grid.
|
|
52
|
+
*/
|
|
53
|
+
export function hasTerminalMultiplexerSession(
|
|
54
|
+
multiplexer: TerminalMultiplexer,
|
|
55
|
+
env: NodeJS.ProcessEnv = Bun.env,
|
|
56
|
+
): boolean {
|
|
57
|
+
return MULTIPLEXERS_BY_ID[multiplexer].isInside(env);
|
|
11
58
|
}
|
|
12
59
|
|
|
13
|
-
/**
|
|
14
|
-
|
|
60
|
+
/**
|
|
61
|
+
* Every multiplexer whose explicit session markers are present, in registry
|
|
62
|
+
* order. Nested sessions all appear; TERM is not consulted.
|
|
63
|
+
*/
|
|
64
|
+
export function terminalMultiplexerSessions(
|
|
65
|
+
env: NodeJS.ProcessEnv = Bun.env,
|
|
66
|
+
): TerminalMultiplexerModule<TerminalMultiplexer>[] {
|
|
67
|
+
return TERMINAL_MULTIPLEXERS.filter(multiplexer => multiplexer.isInside(env));
|
|
68
|
+
}
|
|
15
69
|
|
|
16
70
|
/**
|
|
17
|
-
*
|
|
18
|
-
*
|
|
71
|
+
* The multiplexer TERM names, whether or not its session markers survived.
|
|
72
|
+
* This is classification's TERM fallback; session markers are not consulted.
|
|
73
|
+
*/
|
|
74
|
+
export function terminalMultiplexerForTerm(
|
|
75
|
+
env: NodeJS.ProcessEnv = Bun.env,
|
|
76
|
+
): TerminalMultiplexerModule<TerminalMultiplexer> | undefined {
|
|
77
|
+
const term = env.TERM?.toLowerCase() ?? "";
|
|
78
|
+
return TERMINAL_MULTIPLEXERS.find(
|
|
79
|
+
multiplexer => multiplexer.termPrefix !== undefined && term.startsWith(multiplexer.termPrefix),
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Registry entry for the multiplexer hosting the current process, or
|
|
85
|
+
* `undefined` for a direct terminal. Same precedence as
|
|
86
|
+
* {@link classifyTerminalMultiplexer}.
|
|
87
|
+
*/
|
|
88
|
+
export function classifyTerminalMultiplexerModule(
|
|
89
|
+
env: NodeJS.ProcessEnv = Bun.env,
|
|
90
|
+
): TerminalMultiplexerModule<TerminalMultiplexer> | undefined {
|
|
91
|
+
return (
|
|
92
|
+
SESSION_TIER.find(multiplexer => multiplexer.isInside(env)) ??
|
|
93
|
+
terminalMultiplexerForTerm(env) ??
|
|
94
|
+
OUTER_APP_TIER.find(multiplexer => multiplexer.isInside(env))
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Classify which terminal multiplexer hosts the current process, or `null` for
|
|
100
|
+
* a direct terminal. Single source of truth for both the render-path gate
|
|
19
101
|
* (`isInsideTerminalMultiplexer`) and the debug snapshot label.
|
|
20
102
|
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* overrides and can be set outside a CMUX/WMUX terminal. wmux is a Windows
|
|
26
|
-
* multiplexer (Electron + xterm.js) modeled on cmux/herdr that repaints its
|
|
27
|
-
* pane in place and exports WMUX=1 plus a native WMUX_SURFACE_ID.
|
|
103
|
+
* Session markers are authoritative. TERM can also survive when those markers
|
|
104
|
+
* are stripped (`sudo` without -E, `su`, env-sanitizing launchers/ssh), so a
|
|
105
|
+
* `tmux`/`screen` TERM prefix comes next. Outer applications such as Orca rank
|
|
106
|
+
* last so a multiplexer running inside them wins.
|
|
28
107
|
*/
|
|
29
108
|
export function classifyTerminalMultiplexer(env: NodeJS.ProcessEnv = Bun.env): TerminalMultiplexer | null {
|
|
30
|
-
|
|
31
|
-
if (env.TMUX) return "tmux";
|
|
32
|
-
if (env.STY) return "screen";
|
|
33
|
-
if (env.ZELLIJ) return "zellij";
|
|
34
|
-
if (env.CMUX_WORKSPACE_ID || env.CMUX_SURFACE_ID || env.CMUX_REMOTE_TRANSPORT) return "cmux";
|
|
35
|
-
if (env.WMUX === "1" || env.WMUX_SURFACE_ID) return "wmux";
|
|
36
|
-
const term = env.TERM?.toLowerCase() ?? "";
|
|
37
|
-
if (term.startsWith("tmux")) return "tmux";
|
|
38
|
-
if (term.startsWith("screen")) return "screen";
|
|
39
|
-
return null;
|
|
109
|
+
return classifyTerminalMultiplexerModule(env)?.id ?? null;
|
|
40
110
|
}
|
|
41
111
|
|
|
42
|
-
/** True when
|
|
112
|
+
/** True when the classified multiplexer owns the current screen grid. */
|
|
43
113
|
export function isInsideTerminalMultiplexer(env: NodeJS.ProcessEnv = Bun.env): boolean {
|
|
44
|
-
return
|
|
114
|
+
return classifyTerminalMultiplexerModule(env)?.ownsScreenGrid ?? false;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Offer a notification to every active session's notifier, innermost tier
|
|
119
|
+
* first. Returns whether one delivered it; otherwise the terminal fallback
|
|
120
|
+
* applies unchanged.
|
|
121
|
+
*/
|
|
122
|
+
export function routeTerminalMultiplexerNotification(request: TerminalMultiplexerNotificationRequest): boolean {
|
|
123
|
+
return NOTIFIERS.some(({ multiplexer, notifier }) => multiplexer.isInside(request.env) && notifier.send(request));
|
|
45
124
|
}
|
package/src/terminal.d.ts
CHANGED
|
@@ -128,13 +128,21 @@ export type TspHelloHandler = (hello: TspHello | null) => void;
|
|
|
128
128
|
export type CursorShape = "default" | "block" | "underline" | "bar";
|
|
129
129
|
export declare const CURSOR_SHAPE_CODES: Record<CursorShape, number>;
|
|
130
130
|
export interface Terminal {
|
|
131
|
+
/**
|
|
132
|
+
* Start the terminal with input, resize, and host-disconnect handlers.
|
|
133
|
+
* Unless input is deferred, this sends capability probes; a terminal that
|
|
134
|
+
* cannot parse one may leave concealed bytes on the cursor row, so the
|
|
135
|
+
* caller repaints after starting.
|
|
136
|
+
*/
|
|
131
137
|
start(onInput: (data: string) => void, onResize: () => void, onDisconnect?: () => void, options?: TerminalStartOptions): void;
|
|
132
138
|
/**
|
|
133
139
|
* Take ownership of stdin after a `deferInput` start: enable raw mode,
|
|
134
140
|
* attach input handlers, and run the capability probes start() skipped.
|
|
135
141
|
* Bytes the user typed in cooked mode meanwhile are replayed through
|
|
136
|
-
* `onInput`. No-op when input was never deferred.
|
|
137
|
-
*
|
|
142
|
+
* `onInput`. No-op when input was never deferred. Like {@link start}, the
|
|
143
|
+
* probes may leave concealed bytes on the cursor row, so the caller
|
|
144
|
+
* repaints afterwards. Optional so custom Terminals built against older
|
|
145
|
+
* pi-tui versions keep working.
|
|
138
146
|
*/
|
|
139
147
|
enableInput?(): void;
|
|
140
148
|
stop(): void;
|
|
@@ -222,6 +230,12 @@ export interface Terminal {
|
|
|
222
230
|
* `status` is the DECRPM value when the terminal answered DECRQM.
|
|
223
231
|
*/
|
|
224
232
|
onPrivateModeReport?(callback: PrivateModeReportHandler): void;
|
|
233
|
+
/**
|
|
234
|
+
* Report whether DA1 advertises SIXEL (attribute 4), replaying the latest
|
|
235
|
+
* advertisement to late subscribers. A missing attribute does not rule out
|
|
236
|
+
* support discovered through another graphics query.
|
|
237
|
+
*/
|
|
238
|
+
onSixelSupport?(callback: (supported: boolean) => void): void;
|
|
225
239
|
/**
|
|
226
240
|
* Register a callback fired once the startup Glyph Protocol handshake
|
|
227
241
|
* resolves (see {@link GlyphProtocolReportHandler}). A subscriber that
|
|
@@ -290,10 +304,11 @@ export declare class ProcessTerminal implements Terminal {
|
|
|
290
304
|
onAppearanceReport(callback: (appearance: TerminalAppearance, requestToken?: TerminalAppearanceRequestToken) => void): () => void;
|
|
291
305
|
/**
|
|
292
306
|
* Re-query the terminal background through the startup DA1-sentinel FIFO,
|
|
293
|
-
* pending/queued gating, parsing, dedup, and appearance callbacks. Inside
|
|
294
|
-
*
|
|
295
|
-
*
|
|
296
|
-
*
|
|
307
|
+
* pending/queued gating, parsing, dedup, and appearance callbacks. Inside a
|
|
308
|
+
* multiplexer that answers OSC 11 from a cache (tmux), only this explicit
|
|
309
|
+
* path first passes an OSC 11 query to the outer terminal, waits briefly for
|
|
310
|
+
* the multiplexer to consume the response into its cache, then reads that
|
|
311
|
+
* cache with a direct query. The outer query deliberately has
|
|
297
312
|
* no DA1 sentinel: multiplexers can decode a fragmented DA1 response as a key
|
|
298
313
|
* sequence and leak the remaining bytes into the editor. Startup and Mode 2031
|
|
299
314
|
* probes remain direct. Suppressed while inactive, headless, or after teardown.
|
|
@@ -301,6 +316,7 @@ export declare class ProcessTerminal implements Terminal {
|
|
|
301
316
|
refreshAppearance(requestToken?: TerminalAppearanceRequestToken): TerminalAppearanceRequestToken | void;
|
|
302
317
|
onPrivateModeReport(callback: PrivateModeReportHandler): void;
|
|
303
318
|
onGlyphProtocolReport(callback: GlyphProtocolReportHandler): void;
|
|
319
|
+
onSixelSupport(callback: (supported: boolean) => void): void;
|
|
304
320
|
onTspHello(callback: TspHelloHandler): void;
|
|
305
321
|
get tspProbePending(): boolean;
|
|
306
322
|
get tspExpected(): boolean;
|
package/src/terminal.ts
CHANGED
|
@@ -3,6 +3,7 @@ import * as fs from "node:fs";
|
|
|
3
3
|
import { TtyWriter } from "@linxiraos/pi-natives";
|
|
4
4
|
import { $env, isBunTestRuntime, isTerminalHeadless, isWsl } from "@linxiraos/pi-utils/env";
|
|
5
5
|
import * as logger from "@linxiraos/pi-utils/logger";
|
|
6
|
+
import { popLoopPhase, pushLoopPhase } from "@linxiraos/pi-utils/loop-phase";
|
|
6
7
|
import * as postmortem from "@linxiraos/pi-utils/postmortem";
|
|
7
8
|
import { restoreTerminalStderr, suppressTerminalStderr } from "@linxiraos/pi-utils/stderr-guard";
|
|
8
9
|
import { TSP_VERSION } from "@linxiraos/pi-wire";
|
|
@@ -27,7 +28,10 @@ import {
|
|
|
27
28
|
setTerminalGlyphProtocol,
|
|
28
29
|
TERMINAL,
|
|
29
30
|
} from "./terminal-capabilities";
|
|
30
|
-
import {
|
|
31
|
+
import { tmuxMultiplexer } from "./multiplexers/tmux";
|
|
32
|
+
import type { TerminalMultiplexerBackgroundColorCache } from "./multiplexers/types";
|
|
33
|
+
import { terminalMultiplexerSessions } from "./terminal-multiplexer";
|
|
34
|
+
import { setTmuxPaneApcTitles, tmuxSessionSpeaksTsp } from "./tmux";
|
|
31
35
|
import { setHangulCompatibilityJamoWidth } from "./utils";
|
|
32
36
|
import { translateWindowsAltGrSequence } from "./windows-altgr";
|
|
33
37
|
import { Win32InputModeDecoder, Win32PasteMarkerNormalizer } from "./windows-input-mode";
|
|
@@ -48,6 +52,9 @@ const WIN32_RELAYED_ESCAPE_TIMEOUT_MS = 75;
|
|
|
48
52
|
*/
|
|
49
53
|
const TSP_OSC_PREFIX = `\x1b]877;${TSP_PREFIX.slice(2)}`;
|
|
50
54
|
|
|
55
|
+
/** How long a TSP hello waits for its reply inside tmux, which answers DA1 itself. */
|
|
56
|
+
const TSP_TMUX_HELLO_TIMEOUT_MS = 1000;
|
|
57
|
+
|
|
51
58
|
/**
|
|
52
59
|
* The TSP message a reassembled input string holds, in APC form, or
|
|
53
60
|
* `undefined` while its terminator has not arrived. An OSC 877 message may end
|
|
@@ -501,7 +508,12 @@ export const CURSOR_SHAPE_CODES: Record<CursorShape, number> = {
|
|
|
501
508
|
bar: 6,
|
|
502
509
|
};
|
|
503
510
|
export interface Terminal {
|
|
504
|
-
|
|
511
|
+
/**
|
|
512
|
+
* Start the terminal with input, resize, and host-disconnect handlers.
|
|
513
|
+
* Unless input is deferred, this sends capability probes; a terminal that
|
|
514
|
+
* cannot parse one may leave concealed bytes on the cursor row, so the
|
|
515
|
+
* caller repaints after starting.
|
|
516
|
+
*/
|
|
505
517
|
start(
|
|
506
518
|
onInput: (data: string) => void,
|
|
507
519
|
onResize: () => void,
|
|
@@ -513,8 +525,10 @@ export interface Terminal {
|
|
|
513
525
|
* Take ownership of stdin after a `deferInput` start: enable raw mode,
|
|
514
526
|
* attach input handlers, and run the capability probes start() skipped.
|
|
515
527
|
* Bytes the user typed in cooked mode meanwhile are replayed through
|
|
516
|
-
* `onInput`. No-op when input was never deferred.
|
|
517
|
-
*
|
|
528
|
+
* `onInput`. No-op when input was never deferred. Like {@link start}, the
|
|
529
|
+
* probes may leave concealed bytes on the cursor row, so the caller
|
|
530
|
+
* repaints afterwards. Optional so custom Terminals built against older
|
|
531
|
+
* pi-tui versions keep working.
|
|
518
532
|
*/
|
|
519
533
|
enableInput?(): void;
|
|
520
534
|
|
|
@@ -646,6 +660,12 @@ export interface Terminal {
|
|
|
646
660
|
* `status` is the DECRPM value when the terminal answered DECRQM.
|
|
647
661
|
*/
|
|
648
662
|
onPrivateModeReport?(callback: PrivateModeReportHandler): void;
|
|
663
|
+
/**
|
|
664
|
+
* Report whether DA1 advertises SIXEL (attribute 4), replaying the latest
|
|
665
|
+
* advertisement to late subscribers. A missing attribute does not rule out
|
|
666
|
+
* support discovered through another graphics query.
|
|
667
|
+
*/
|
|
668
|
+
onSixelSupport?(callback: (supported: boolean) => void): void;
|
|
649
669
|
/**
|
|
650
670
|
* Register a callback fired once the startup Glyph Protocol handshake
|
|
651
671
|
* resolves (see {@link GlyphProtocolReportHandler}). A subscriber that
|
|
@@ -707,9 +727,18 @@ function parseOsc99KeyValues(section: string): Map<string, string> {
|
|
|
707
727
|
}
|
|
708
728
|
return values;
|
|
709
729
|
}
|
|
730
|
+
/**
|
|
731
|
+
* Brackets the startup capability probes. A terminal that cannot parse a probe
|
|
732
|
+
* prints it as text at the cursor (Apple Terminal drops `ESC _`/`ESC \` and
|
|
733
|
+
* prints APC payloads, and abandons DECRQM at its `$`, printing the final `p`).
|
|
734
|
+
* DECSC, autowrap off, and SGR 8 (conceal) keep that text invisible and on the
|
|
735
|
+
* cursor row, so it can neither wrap nor scroll the screen; DECRC puts the
|
|
736
|
+
* cursor back. The screen owner repaints the row afterwards (see
|
|
737
|
+
* {@link Terminal.start} / {@link Terminal.enableInput}).
|
|
738
|
+
*/
|
|
739
|
+
const PROBE_GUARD_BEGIN = "\x1b7\x1b[?7l\x1b[8m";
|
|
740
|
+
const PROBE_GUARD_END = "\x1b[28m\x1b[?7h\x1b8";
|
|
710
741
|
const XTERM_SCROLL_TO_BOTTOM_MODES = [1010, 1011] as const;
|
|
711
|
-
type Osc11QueryRoute = "direct" | "tmux";
|
|
712
|
-
const TMUX_OSC11_CACHE_REFRESH_DELAY_MS = 100;
|
|
713
742
|
|
|
714
743
|
function isXtermScrollToBottomMode(mode: number): boolean {
|
|
715
744
|
return mode === 1010 || mode === 1011;
|
|
@@ -830,10 +859,10 @@ export class ProcessTerminal implements Terminal {
|
|
|
830
859
|
#appearance: TerminalAppearance | undefined;
|
|
831
860
|
#osc11Pending = false;
|
|
832
861
|
#osc11ActiveToken?: TerminalAppearanceRequestToken;
|
|
833
|
-
#osc11QueuedQuery?: {
|
|
862
|
+
#osc11QueuedQuery?: { cache?: TerminalMultiplexerBackgroundColorCache; token?: TerminalAppearanceRequestToken };
|
|
834
863
|
#nextAppearanceRequestToken = 1;
|
|
835
864
|
#osc11ResponseBuffer = "";
|
|
836
|
-
#
|
|
865
|
+
#osc11CacheRefreshTimer?: Timer;
|
|
837
866
|
#osc99PendingId: string | undefined;
|
|
838
867
|
#osc99ResponseBuffer = "";
|
|
839
868
|
#osc99Capabilities = new Map<string, string>();
|
|
@@ -845,9 +874,15 @@ export class ProcessTerminal implements Terminal {
|
|
|
845
874
|
#glyphProtocolReplyBuffer = "";
|
|
846
875
|
#glyphProtocolResult: boolean | undefined;
|
|
847
876
|
#glyphProtocolCallbacks: GlyphProtocolReportHandler[] = [];
|
|
877
|
+
#sixelSupport: boolean | undefined;
|
|
878
|
+
#sixelSupportCallbacks: Array<(supported: boolean) => void> = [];
|
|
848
879
|
#tspPending = false;
|
|
849
880
|
#tspResult: TspHello | null | undefined;
|
|
850
881
|
#tspCallbacks: TspHelloHandler[] = [];
|
|
882
|
+
/** Hello deadline inside tmux, where no DA1 sentinel can end the probe. */
|
|
883
|
+
#tspTimeout: NodeJS.Timeout | undefined;
|
|
884
|
+
/** This tmux pane's APC titles are off for TSP and must be restored. */
|
|
885
|
+
#tspTmuxPaneGated = false;
|
|
851
886
|
#tspReplyBuffer = "";
|
|
852
887
|
#privateCsiResponseBuffer = "";
|
|
853
888
|
#da1SentinelOwners: Da1SentinelOwner[] = [];
|
|
@@ -931,10 +966,11 @@ export class ProcessTerminal implements Terminal {
|
|
|
931
966
|
|
|
932
967
|
/**
|
|
933
968
|
* Re-query the terminal background through the startup DA1-sentinel FIFO,
|
|
934
|
-
* pending/queued gating, parsing, dedup, and appearance callbacks. Inside
|
|
935
|
-
*
|
|
936
|
-
*
|
|
937
|
-
*
|
|
969
|
+
* pending/queued gating, parsing, dedup, and appearance callbacks. Inside a
|
|
970
|
+
* multiplexer that answers OSC 11 from a cache (tmux), only this explicit
|
|
971
|
+
* path first passes an OSC 11 query to the outer terminal, waits briefly for
|
|
972
|
+
* the multiplexer to consume the response into its cache, then reads that
|
|
973
|
+
* cache with a direct query. The outer query deliberately has
|
|
938
974
|
* no DA1 sentinel: multiplexers can decode a fragmented DA1 response as a key
|
|
939
975
|
* sequence and leak the remaining bytes into the editor. Startup and Mode 2031
|
|
940
976
|
* probes remain direct. Suppressed while inactive, headless, or after teardown.
|
|
@@ -945,7 +981,10 @@ export class ProcessTerminal implements Terminal {
|
|
|
945
981
|
if (token >= this.#nextAppearanceRequestToken) {
|
|
946
982
|
this.#nextAppearanceRequestToken = token + 1;
|
|
947
983
|
}
|
|
948
|
-
|
|
984
|
+
const cache = terminalMultiplexerSessions().find(
|
|
985
|
+
multiplexer => multiplexer.backgroundColorCache,
|
|
986
|
+
)?.backgroundColorCache;
|
|
987
|
+
this.#queryBackgroundColor(cache, token);
|
|
949
988
|
return token;
|
|
950
989
|
}
|
|
951
990
|
|
|
@@ -960,6 +999,11 @@ export class ProcessTerminal implements Terminal {
|
|
|
960
999
|
if (this.#glyphProtocolResult !== undefined) callback(this.#glyphProtocolResult);
|
|
961
1000
|
}
|
|
962
1001
|
|
|
1002
|
+
onSixelSupport(callback: (supported: boolean) => void): void {
|
|
1003
|
+
this.#sixelSupportCallbacks.push(callback);
|
|
1004
|
+
if (this.#sixelSupport !== undefined) callback(this.#sixelSupport);
|
|
1005
|
+
}
|
|
1006
|
+
|
|
963
1007
|
onTspHello(callback: TspHelloHandler): void {
|
|
964
1008
|
this.#tspCallbacks.push(callback);
|
|
965
1009
|
if (this.#tspResult !== undefined) callback(this.#tspResult);
|
|
@@ -1099,6 +1143,8 @@ export class ProcessTerminal implements Terminal {
|
|
|
1099
1143
|
// events that lose modifier information. Must run after setRawMode(true)
|
|
1100
1144
|
// since that resets console mode flags.
|
|
1101
1145
|
this.#enableWindowsVTInput();
|
|
1146
|
+
// Every response-eliciting probe goes out inside the leak guard.
|
|
1147
|
+
this.#safeWrite(PROBE_GUARD_BEGIN);
|
|
1102
1148
|
// Query and enable Kitty keyboard protocol
|
|
1103
1149
|
// The query handler intercepts input temporarily, then installs the user's handler
|
|
1104
1150
|
// See: https://sw.kovidgoyal.net/kitty/keyboard-protocol/
|
|
@@ -1161,6 +1207,7 @@ export class ProcessTerminal implements Terminal {
|
|
|
1161
1207
|
for (const mode of XTERM_SCROLL_TO_BOTTOM_MODES) {
|
|
1162
1208
|
this.#queryPrivateMode(mode);
|
|
1163
1209
|
}
|
|
1210
|
+
this.#safeWrite(PROBE_GUARD_END);
|
|
1164
1211
|
}
|
|
1165
1212
|
|
|
1166
1213
|
/**
|
|
@@ -1241,6 +1288,7 @@ export class ProcessTerminal implements Terminal {
|
|
|
1241
1288
|
|
|
1242
1289
|
// DA1 (Primary Device Attributes) response: \x1b[?...c
|
|
1243
1290
|
const da1ResponsePattern = /^\x1b\[\?[\d;]*c$/;
|
|
1291
|
+
const da1SixelAttributePattern = /;4(?:;|c$)/u;
|
|
1244
1292
|
|
|
1245
1293
|
// Private CSI partial: \x1b[?<digits/semicolons>... — incomplete probe response
|
|
1246
1294
|
// that the StdinBuffer flushed before the terminator arrived (split across
|
|
@@ -1384,6 +1432,19 @@ export class ProcessTerminal implements Terminal {
|
|
|
1384
1432
|
// outstanding sentinel — a reply that arrives after the FIFO drains (slow
|
|
1385
1433
|
// SSH/PTY links) must never reach the composer as literal text (#8542).
|
|
1386
1434
|
if (da1ResponsePattern.test(sequence)) {
|
|
1435
|
+
// Publish graphics support without forwarding DA1 bytes to application
|
|
1436
|
+
// input or changing ownership of the existing probe sentinel.
|
|
1437
|
+
const supportsSixel = da1SixelAttributePattern.test(sequence);
|
|
1438
|
+
if (supportsSixel !== this.#sixelSupport) {
|
|
1439
|
+
this.#sixelSupport = supportsSixel;
|
|
1440
|
+
for (const callback of this.#sixelSupportCallbacks) {
|
|
1441
|
+
try {
|
|
1442
|
+
callback(supportsSixel);
|
|
1443
|
+
} catch {
|
|
1444
|
+
// Capability subscribers must not interrupt sentinel handling.
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1387
1448
|
const owner = this.#da1SentinelOwners.shift();
|
|
1388
1449
|
if (!owner) {
|
|
1389
1450
|
// Late/unowned reply: nothing to resolve, just drop the bytes.
|
|
@@ -1406,7 +1467,7 @@ export class ProcessTerminal implements Terminal {
|
|
|
1406
1467
|
) {
|
|
1407
1468
|
const query = this.#osc11QueuedQuery;
|
|
1408
1469
|
this.#osc11QueuedQuery = undefined;
|
|
1409
|
-
this.#startOsc11Query(query.
|
|
1470
|
+
this.#startOsc11Query(query.cache, query.token);
|
|
1410
1471
|
}
|
|
1411
1472
|
break;
|
|
1412
1473
|
}
|
|
@@ -1624,36 +1685,39 @@ export class ProcessTerminal implements Terminal {
|
|
|
1624
1685
|
* DA1 avoids indefinite hangs: if DA1 response arrives before OSC 11,
|
|
1625
1686
|
* the terminal does not support OSC 11.
|
|
1626
1687
|
*/
|
|
1627
|
-
#queryBackgroundColor(
|
|
1688
|
+
#queryBackgroundColor(
|
|
1689
|
+
cache?: TerminalMultiplexerBackgroundColorCache,
|
|
1690
|
+
token?: TerminalAppearanceRequestToken,
|
|
1691
|
+
): void {
|
|
1628
1692
|
if (this.#dead) return;
|
|
1629
1693
|
// Queue if an OSC 11 query is in flight or its DA1 sentinel hasn't been
|
|
1630
1694
|
// consumed yet. Starting a new query while a DA1 is outstanding would
|
|
1631
1695
|
// increment the sentinel counter, and the old DA1 arrival would then
|
|
1632
1696
|
// prematurely clear the new query's pending state. Preserve a requested
|
|
1633
|
-
//
|
|
1634
|
-
//
|
|
1697
|
+
// cache refresh when coalescing direct and explicit queries, and retain
|
|
1698
|
+
// the latest explicit request identity across automatic queries.
|
|
1635
1699
|
if (this.#osc11Pending || this.#da1SentinelOwners.some(o => o.kind === "osc11")) {
|
|
1636
1700
|
const queued = this.#osc11QueuedQuery;
|
|
1637
|
-
this.#osc11QueuedQuery = {
|
|
1638
|
-
route: queued?.route === "tmux" || route === "tmux" ? "tmux" : "direct",
|
|
1639
|
-
token: token ?? queued?.token,
|
|
1640
|
-
};
|
|
1701
|
+
this.#osc11QueuedQuery = { cache: queued?.cache ?? cache, token: token ?? queued?.token };
|
|
1641
1702
|
return;
|
|
1642
1703
|
}
|
|
1643
|
-
this.#startOsc11Query(
|
|
1704
|
+
this.#startOsc11Query(cache, token);
|
|
1644
1705
|
}
|
|
1645
1706
|
|
|
1646
|
-
#startOsc11Query(
|
|
1707
|
+
#startOsc11Query(
|
|
1708
|
+
cache: TerminalMultiplexerBackgroundColorCache | undefined,
|
|
1709
|
+
token?: TerminalAppearanceRequestToken,
|
|
1710
|
+
): void {
|
|
1647
1711
|
this.#osc11Pending = true;
|
|
1648
1712
|
this.#osc11ActiveToken = token;
|
|
1649
1713
|
this.#osc11ResponseBuffer = "";
|
|
1650
|
-
if (
|
|
1651
|
-
this.#safeWrite(
|
|
1652
|
-
this.#
|
|
1653
|
-
this.#
|
|
1714
|
+
if (cache) {
|
|
1715
|
+
this.#safeWrite(cache.passthrough("\x1b]11;?\x07"));
|
|
1716
|
+
this.#osc11CacheRefreshTimer = setTimeout(() => {
|
|
1717
|
+
this.#osc11CacheRefreshTimer = undefined;
|
|
1654
1718
|
if (this.#dead || !this.#osc11Pending) return;
|
|
1655
1719
|
this.#startDirectOsc11Query();
|
|
1656
|
-
},
|
|
1720
|
+
}, cache.settleMs);
|
|
1657
1721
|
return;
|
|
1658
1722
|
}
|
|
1659
1723
|
this.#startDirectOsc11Query();
|
|
@@ -1776,6 +1840,9 @@ export class ProcessTerminal implements Terminal {
|
|
|
1776
1840
|
#shouldQueryTspSupport(): boolean {
|
|
1777
1841
|
const override = $env.PI_TUI_NATIVE;
|
|
1778
1842
|
if (override === "0") return false;
|
|
1843
|
+
// tmux makes APC bodies the pane title unless the hosting terminal asked
|
|
1844
|
+
// for TSP through `@tern-tsp`: no override may force the probe there.
|
|
1845
|
+
if (tmuxMultiplexer.isInside($env)) return tmuxSessionSpeaksTsp($env);
|
|
1779
1846
|
if (override === "1") return true;
|
|
1780
1847
|
// Multiplexers swallow APC, so the reply could never arrive.
|
|
1781
1848
|
if (isInsideTerminalMultiplexer($env)) return false;
|
|
@@ -1793,16 +1860,37 @@ export class ProcessTerminal implements Terminal {
|
|
|
1793
1860
|
return;
|
|
1794
1861
|
}
|
|
1795
1862
|
this.#tspPending = true;
|
|
1863
|
+
if (tmuxMultiplexer.isInside($env)) {
|
|
1864
|
+
// tmux answers DA1 itself, ahead of any reply: wait for the reply instead.
|
|
1865
|
+
this.#tspTmuxPaneGated = true;
|
|
1866
|
+
setTmuxPaneApcTitles(false, $env);
|
|
1867
|
+
this.#tspTimeout = setTimeout(() => {
|
|
1868
|
+
this.#tspTimeout = undefined;
|
|
1869
|
+
this.#resolveTspSupport(null);
|
|
1870
|
+
}, TSP_TMUX_HELLO_TIMEOUT_MS);
|
|
1871
|
+
this.#safeWrite(encodeTspHelloQuery());
|
|
1872
|
+
return;
|
|
1873
|
+
}
|
|
1796
1874
|
this.#da1SentinelOwners.push({ kind: "tsp" });
|
|
1797
1875
|
this.#safeWrite(`${encodeTspHelloQuery()}\x1b[c`);
|
|
1798
1876
|
}
|
|
1799
1877
|
|
|
1878
|
+
/** Hand APC titles back to tmux once this pane stops speaking TSP. */
|
|
1879
|
+
#endTspTmuxPane(): void {
|
|
1880
|
+
clearTimeout(this.#tspTimeout);
|
|
1881
|
+
this.#tspTimeout = undefined;
|
|
1882
|
+
if (!this.#tspTmuxPaneGated) return;
|
|
1883
|
+
this.#tspTmuxPaneGated = false;
|
|
1884
|
+
setTmuxPaneApcTitles(true, $env);
|
|
1885
|
+
}
|
|
1886
|
+
|
|
1800
1887
|
#handleTspMessage(sequence: string): void {
|
|
1801
1888
|
const message = parseTspMessage(sequence);
|
|
1802
|
-
if (message?.verb === "r") {
|
|
1803
|
-
|
|
1889
|
+
if (message?.verb === "r" && message.reply.r === "hello") {
|
|
1890
|
+
this.#resolveTspSupport(message.reply);
|
|
1804
1891
|
return;
|
|
1805
1892
|
}
|
|
1893
|
+
// Events and every other reply (`blobs`) go to the native backend.
|
|
1806
1894
|
this.#inputHandler?.(sequence);
|
|
1807
1895
|
}
|
|
1808
1896
|
|
|
@@ -1811,6 +1899,12 @@ export class ProcessTerminal implements Terminal {
|
|
|
1811
1899
|
this.#tspPending = false;
|
|
1812
1900
|
const result = hello !== null && hello.v === TSP_VERSION ? hello : null;
|
|
1813
1901
|
if (hello !== null && result === null) logger.warn("TSP: unsupported protocol version", { v: hello.v });
|
|
1902
|
+
if (result === null) {
|
|
1903
|
+
this.#endTspTmuxPane();
|
|
1904
|
+
} else {
|
|
1905
|
+
clearTimeout(this.#tspTimeout);
|
|
1906
|
+
this.#tspTimeout = undefined;
|
|
1907
|
+
}
|
|
1814
1908
|
this.#tspResult = result;
|
|
1815
1909
|
for (const cb of this.#tspCallbacks) {
|
|
1816
1910
|
try {
|
|
@@ -2176,9 +2270,9 @@ export class ProcessTerminal implements Terminal {
|
|
|
2176
2270
|
clearTimeout(this.#mode2031DebounceTimer);
|
|
2177
2271
|
this.#mode2031DebounceTimer = undefined;
|
|
2178
2272
|
}
|
|
2179
|
-
if (this.#
|
|
2180
|
-
clearTimeout(this.#
|
|
2181
|
-
this.#
|
|
2273
|
+
if (this.#osc11CacheRefreshTimer) {
|
|
2274
|
+
clearTimeout(this.#osc11CacheRefreshTimer);
|
|
2275
|
+
this.#osc11CacheRefreshTimer = undefined;
|
|
2182
2276
|
}
|
|
2183
2277
|
this.#appearanceCallbacks = [];
|
|
2184
2278
|
this.#appearanceReportCallbacks = [];
|
|
@@ -2195,8 +2289,11 @@ export class ProcessTerminal implements Terminal {
|
|
|
2195
2289
|
this.#glyphProtocolResult = undefined;
|
|
2196
2290
|
this.#glyphProtocolReplyBuffer = "";
|
|
2197
2291
|
this.#glyphProtocolCallbacks = [];
|
|
2292
|
+
this.#sixelSupport = undefined;
|
|
2293
|
+
this.#sixelSupportCallbacks = [];
|
|
2198
2294
|
setTerminalGlyphProtocol(false);
|
|
2199
2295
|
this.#tspPending = false;
|
|
2296
|
+
this.#endTspTmuxPane();
|
|
2200
2297
|
this.#tspResult = undefined;
|
|
2201
2298
|
this.#tspReplyBuffer = "";
|
|
2202
2299
|
this.#tspCallbacks = [];
|
|
@@ -2366,11 +2463,14 @@ export class ProcessTerminal implements Terminal {
|
|
|
2366
2463
|
this.#trackStdoutBacklog(pending);
|
|
2367
2464
|
return;
|
|
2368
2465
|
}
|
|
2369
|
-
//
|
|
2370
|
-
//
|
|
2371
|
-
|
|
2372
|
-
if (process.platform === "win32") ensureWindowsConsoleUtf8();
|
|
2466
|
+
// Without the pump, writes (and the codepage guard's console calls) block
|
|
2467
|
+
// the event loop until the terminal drains; label them for the watchdog.
|
|
2468
|
+
pushLoopPhase("ui.terminal-write");
|
|
2373
2469
|
try {
|
|
2470
|
+
// A console-sharing child process may have flipped the console codepage
|
|
2471
|
+
// away from UTF-8; repair it before any bytes hit WriteFile so no frame
|
|
2472
|
+
// is ever translated through an OEM codepage. See ensureWindowsConsoleUtf8.
|
|
2473
|
+
if (process.platform === "win32") ensureWindowsConsoleUtf8();
|
|
2374
2474
|
// Windows ConPTY drops viewport tracking when a single write exceeds
|
|
2375
2475
|
// ~32-64 KB: the host UI's scroll position stays parked at wherever
|
|
2376
2476
|
// the write began, even though every byte landed in scrollback. Split
|
|
@@ -2399,6 +2499,8 @@ export class ProcessTerminal implements Terminal {
|
|
|
2399
2499
|
this.#trackStdoutBacklog(process.stdout.writableLength ?? 0);
|
|
2400
2500
|
} catch (err) {
|
|
2401
2501
|
this.#markTerminalDisconnected("stdout failed", err);
|
|
2502
|
+
} finally {
|
|
2503
|
+
popLoopPhase();
|
|
2402
2504
|
}
|
|
2403
2505
|
}
|
|
2404
2506
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Active theme's symbol lookup without importing `./theme` (which pulls the
|
|
3
3
|
* native addon through `@linxiraos/pi-natives`). Key-hint formatting runs on
|
|
4
|
-
* CLI paths that must stay addon-free (`
|
|
4
|
+
* CLI paths that must stay addon-free (`zetacode --version`, help), so it reads the
|
|
5
5
|
* active theme through this mirror; `./theme` publishes every assignment here.
|
|
6
6
|
*/
|
|
7
7
|
import { type SymbolKey } from "./symbols";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Active theme's symbol lookup without importing `./theme` (which pulls the
|
|
3
3
|
* native addon through `@linxiraos/pi-natives`). Key-hint formatting runs on
|
|
4
|
-
* CLI paths that must stay addon-free (`
|
|
4
|
+
* CLI paths that must stay addon-free (`zetacode --version`, help), so it reads the
|
|
5
5
|
* active theme through this mirror; `./theme` publishes every assignment here.
|
|
6
6
|
*/
|
|
7
7
|
import { SYMBOL_PRESETS, type SymbolKey } from "./symbols";
|
|
@@ -13,6 +13,7 @@ const themeColorsSchema = type({
|
|
|
13
13
|
muted: "string | number",
|
|
14
14
|
dim: "string | number",
|
|
15
15
|
text: "string | number",
|
|
16
|
+
"assistantMessageText?": "string | number",
|
|
16
17
|
thinkingText: "string | number",
|
|
17
18
|
selectedBg: "string | number",
|
|
18
19
|
userMessageBg: "string | number",
|