@linxiraos/pi-tui 1.1.26 → 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 +51 -491
- package/README.md +13 -7
- package/dist/types/active-terminal.d.ts +30 -0
- package/dist/types/app-keybindings.d.ts +7 -2
- 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 +5 -4
- 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 +11 -1
- package/dist/types/chat/chat-transcript-builder.d.ts +3 -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 +17 -4
- package/dist/types/chrome/shared.d.ts +9 -0
- package/dist/types/chrome/status-notice.d.ts +15 -3
- 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 +23 -5
- package/dist/types/components/form.d.ts +1 -0
- package/dist/types/components/image.d.ts +26 -44
- package/dist/types/components/input.d.ts +2 -1
- 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/loader.d.ts +5 -6
- package/dist/types/components/markdown.d.ts +47 -5
- 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 +9 -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/apply.d.ts +4 -3
- package/dist/types/native/backend.d.ts +5 -4
- package/dist/types/native/blobs.d.ts +7 -3
- package/dist/types/native/encode.d.ts +8 -6
- package/dist/types/native/node.d.ts +36 -5
- package/dist/types/native/overlay.d.ts +8 -3
- package/dist/types/native/settle.d.ts +4 -3
- 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 +3 -0
- package/dist/types/overlays/agent-transcript-viewer.d.ts +12 -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/btw-history-panel.d.ts +13 -1
- package/dist/types/overlays/copy-selector.d.ts +1 -0
- package/dist/types/overlays/history-search.d.ts +10 -0
- package/dist/types/overlays/hook-selector.d.ts +9 -4
- package/dist/types/overlays/hub-frame.d.ts +4 -1
- 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 +37 -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/report-panel.d.ts +68 -0
- package/dist/types/overlays/rewind-selector.d.ts +1 -0
- 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/thinking-selector.d.ts +2 -2
- package/dist/types/overlays/usage-dashboard.d.ts +12 -8
- package/dist/types/overlays/usage-display.d.ts +3 -1
- package/dist/types/prompt/composer-cache.d.ts +0 -16
- package/dist/types/prompt/composer.d.ts +18 -3
- package/dist/types/prompt/custom-editor.d.ts +25 -13
- package/dist/types/prompt/image-source.d.ts +2 -1
- package/dist/types/prompt/interactive-context-helpers.d.ts +5 -1
- package/dist/types/prompt/model-mention-autocomplete.d.ts +1 -1
- package/dist/types/prompt/welcome.d.ts +7 -2
- 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/space-hold.d.ts +40 -25
- package/dist/types/status-line/component.d.ts +3 -0
- package/dist/types/status-line/context-usage.d.ts +7 -5
- 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 +41 -36
- package/dist/types/terminal-multiplexer.d.ts +127 -14
- package/dist/types/terminal.d.ts +23 -16
- 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/thinking.d.ts +1 -1
- package/dist/types/tmux.d.ts +11 -4
- package/dist/types/tools/bash.d.ts +7 -2
- 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 +2 -3
- package/dist/types/tools/write.d.ts +9 -2
- package/dist/types/tools/yield.d.ts +22 -0
- package/dist/types/tui.d.ts +36 -0
- package/dist/types/utils.d.ts +6 -0
- package/dist/types/vim.d.ts +19 -5
- package/dist/types/windows-input-mode.d.ts +33 -1
- package/package.json +13 -9
- package/src/active-terminal.d.ts +30 -0
- package/src/active-terminal.ts +80 -0
- package/src/app-keybindings.d.ts +7 -2
- package/src/app-keybindings.ts +7 -2
- 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 +96 -23
- package/src/apps/git/git-tui.ts +23 -16
- package/src/apps/git/help.ts +29 -20
- package/src/apps/git/sidebar.ts +8 -3
- 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/bracketed-paste.ts +10 -3
- 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 +11 -1
- package/src/chat/assistant-message.ts +245 -54
- package/src/chat/chat-transcript-builder.d.ts +3 -0
- package/src/chat/chat-transcript-builder.ts +9 -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 +86 -7
- package/src/chat/user-message.d.ts +17 -4
- package/src/chat/user-message.ts +71 -8
- package/src/chrome/shared.ts +53 -0
- package/src/chrome/status-notice.ts +25 -7
- package/src/chrome/transcript-container.d.ts +6 -0
- package/src/chrome/transcript-container.ts +162 -19
- 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 +23 -5
- package/src/components/editor.ts +316 -47
- package/src/components/form.d.ts +1 -0
- package/src/components/form.ts +8 -0
- package/src/components/image.d.ts +26 -44
- package/src/components/image.ts +115 -152
- package/src/components/input.d.ts +2 -1
- package/src/components/input.ts +17 -7
- 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/loader.d.ts +5 -6
- package/src/components/loader.ts +10 -17
- package/src/components/markdown.d.ts +47 -5
- package/src/components/markdown.ts +786 -303
- 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 +3 -2
- package/src/fuzzy.d.ts +29 -1
- package/src/fuzzy.ts +115 -30
- package/src/hotkeys-markdown.ts +9 -2
- package/src/key-hint-format.d.ts +9 -1
- package/src/key-hint-format.ts +26 -11
- 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 +11 -5
- 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/apply.d.ts +4 -3
- package/src/native/apply.ts +4 -3
- package/src/native/backend.d.ts +5 -4
- package/src/native/backend.ts +199 -22
- package/src/native/blobs.d.ts +7 -3
- package/src/native/blobs.ts +47 -12
- package/src/native/encode.d.ts +8 -6
- package/src/native/encode.ts +76 -34
- package/src/native/node.d.ts +36 -5
- package/src/native/node.ts +31 -5
- package/src/native/overlay.d.ts +8 -3
- package/src/native/overlay.ts +17 -4
- package/src/native/reconcile.ts +16 -4
- package/src/native/settle.d.ts +4 -3
- package/src/native/settle.ts +4 -3
- 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 +48 -29
- package/src/overlays/agent-transcript-viewer.ts +93 -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 +10 -17
- package/src/overlays/btw-history-panel.ts +136 -39
- package/src/overlays/copy-selector.d.ts +1 -0
- package/src/overlays/copy-selector.ts +32 -8
- package/src/overlays/extensions/extension-dashboard.ts +4 -4
- 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/hub-frame.ts +19 -1
- package/src/overlays/jobs-panel.ts +33 -52
- package/src/overlays/model-browser.ts +367 -122
- package/src/overlays/model-hub.ts +435 -59
- package/src/overlays/move-overlay.ts +28 -14
- package/src/overlays/oauth-selector.ts +54 -70
- package/src/overlays/pause-screen.ts +2 -2
- package/src/overlays/plan-review-overlay.ts +208 -70
- package/src/overlays/plugin-settings.ts +3 -3
- package/src/overlays/report-panel.ts +191 -0
- package/src/overlays/rewind-selector.ts +88 -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 +5 -4
- 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/thinking-selector.ts +11 -10
- package/src/overlays/tree-selector.ts +41 -20
- package/src/overlays/usage-dashboard.ts +117 -58
- package/src/overlays/usage-display.ts +10 -4
- package/src/overlays/usage-row.ts +22 -8
- package/src/prompt/attachment-chips.ts +15 -11
- package/src/prompt/composer-attachments.ts +6 -4
- package/src/prompt/composer-cache.ts +7 -86
- package/src/prompt/composer.ts +37 -26
- package/src/prompt/custom-editor.ts +282 -105
- package/src/prompt/image-source.ts +2 -1
- package/src/prompt/interactive-context-helpers.ts +32 -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/queued-messages.ts +6 -4
- package/src/prompt/tips.txt +13 -9
- package/src/prompt/welcome.ts +64 -46
- 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 +16 -1
- 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/space-hold.d.ts +40 -25
- package/src/space-hold.ts +156 -52
- package/src/status-line/component.ts +292 -78
- package/src/status-line/context-usage.ts +21 -46
- 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 +41 -36
- package/src/terminal-capabilities.ts +137 -213
- package/src/terminal-multiplexer.d.ts +127 -14
- package/src/terminal-multiplexer.ts +112 -33
- package/src/terminal.d.ts +23 -16
- package/src/terminal.ts +203 -99
- 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 +11 -4
- 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/thinking.d.ts +1 -1
- package/src/thinking.ts +1 -1
- 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.d.ts +7 -2
- package/src/tools/bash.ts +116 -47
- 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 +154 -50
- package/src/tools/task.d.ts +2 -3
- package/src/tools/task.ts +115 -62
- 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 +36 -0
- package/src/tui.ts +666 -217
- 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.d.ts +33 -1
- package/src/windows-input-mode.ts +212 -6
package/src/terminal.ts
CHANGED
|
@@ -3,9 +3,11 @@ 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";
|
|
10
|
+
import { getActiveTerminal, registerStdoutErrorHandler, setActiveTerminal } from "./active-terminal";
|
|
9
11
|
import {
|
|
10
12
|
encodeBundledGlyphRegistrations,
|
|
11
13
|
encodeGlyphCoverageQuery,
|
|
@@ -26,15 +28,22 @@ import {
|
|
|
26
28
|
setTerminalGlyphProtocol,
|
|
27
29
|
TERMINAL,
|
|
28
30
|
} from "./terminal-capabilities";
|
|
29
|
-
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";
|
|
30
35
|
import { setHangulCompatibilityJamoWidth } from "./utils";
|
|
31
36
|
import { translateWindowsAltGrSequence } from "./windows-altgr";
|
|
32
|
-
import { Win32InputModeDecoder } from "./windows-input-mode";
|
|
37
|
+
import { Win32InputModeDecoder, Win32PasteMarkerNormalizer } from "./windows-input-mode";
|
|
38
|
+
|
|
39
|
+
export { writeTerminalSequence, writeThroughActiveTerminal } from "./active-terminal";
|
|
33
40
|
|
|
34
41
|
const TERMINAL_PROGRESS_KEEPALIVE_MS = 1000;
|
|
35
42
|
const TERMINAL_PROGRESS_ACTIVE_SEQUENCE = "\x1b]9;4;3\x07";
|
|
36
43
|
const TERMINAL_PROGRESS_CLEAR_SEQUENCE = "\x1b]9;4;0;\x07";
|
|
37
44
|
const WINDOWS_TERMINAL_OSC11_POLL_MS = 30_000;
|
|
45
|
+
/** How long a relayed ESC record waits for the rest of its sequence before acting as Escape (matches StdinBuffer). */
|
|
46
|
+
const WIN32_RELAYED_ESCAPE_TIMEOUT_MS = 75;
|
|
38
47
|
|
|
39
48
|
/**
|
|
40
49
|
* Terminal → program TSP messages through a Windows ConPTY: its input parser
|
|
@@ -43,6 +52,9 @@ const WINDOWS_TERMINAL_OSC11_POLL_MS = 30_000;
|
|
|
43
52
|
*/
|
|
44
53
|
const TSP_OSC_PREFIX = `\x1b]877;${TSP_PREFIX.slice(2)}`;
|
|
45
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
|
+
|
|
46
58
|
/**
|
|
47
59
|
* The TSP message a reassembled input string holds, in APC form, or
|
|
48
60
|
* `undefined` while its terminator has not arrived. An OSC 877 message may end
|
|
@@ -90,6 +102,9 @@ function shouldPollWindowsTerminalAppearance(env: NodeJS.ProcessEnv = Bun.env):
|
|
|
90
102
|
*/
|
|
91
103
|
const MAX_CONPTY_WRITE_CHUNK_BYTES = 16 * 1024;
|
|
92
104
|
|
|
105
|
+
/** An OSC 52 clipboard write: selection, base64 payload, BEL or ST terminator. */
|
|
106
|
+
const OSC52_CLIPBOARD_WRITE = /\x1b\]52;([^;\x07\x1b]*);([^\x07\x1b]*)(\x07|\x1b\\)/g;
|
|
107
|
+
|
|
93
108
|
/**
|
|
94
109
|
* Split `data` into chunks whose encoded UTF-8 byte length is no greater than
|
|
95
110
|
* `maxChunkBytes`, preferring a line boundary (`\n`) as the cut point so
|
|
@@ -280,8 +295,6 @@ export class StdoutStallWatchdog {
|
|
|
280
295
|
* Minimal terminal interface for TUI
|
|
281
296
|
*/
|
|
282
297
|
|
|
283
|
-
// Track active terminal for emergency cleanup on crash
|
|
284
|
-
let activeTerminal: ProcessTerminal | null = null;
|
|
285
298
|
// Track if a terminal was ever started (for emergency restore logic)
|
|
286
299
|
let terminalEverStarted = false;
|
|
287
300
|
// Whether the alternate screen buffer is currently active (mirrors the TUI's
|
|
@@ -306,38 +319,6 @@ function registerPostmortemTerminalRestore(): void {
|
|
|
306
319
|
export function setAltScreenActive(active: boolean): void {
|
|
307
320
|
altScreenActive = active;
|
|
308
321
|
}
|
|
309
|
-
/**
|
|
310
|
-
* Route an out-of-band escape sequence (e.g. an OSC title update) through the
|
|
311
|
-
* active terminal's output path. While a TUI owns stdout, frame paints go
|
|
312
|
-
* through the off-thread write pump and can split across multiple write(2)
|
|
313
|
-
* calls; a direct main-thread `process.stdout.write` can land between two of
|
|
314
|
-
* them — mid escape sequence — and the host terminal then prints the payload
|
|
315
|
-
* as literal text at the cursor position. Returns false when no terminal has
|
|
316
|
-
* started, in which case the caller owns stdout and may write directly.
|
|
317
|
-
*/
|
|
318
|
-
export function writeThroughActiveTerminal(data: string): boolean {
|
|
319
|
-
if (!activeTerminal) return false;
|
|
320
|
-
activeTerminal.write(data);
|
|
321
|
-
return true;
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
const stdoutErrorHandlers = new Set<(err: Error) => void>();
|
|
325
|
-
let stdoutErrorListenerInstalled = false;
|
|
326
|
-
|
|
327
|
-
function onStdoutError(err: Error): void {
|
|
328
|
-
for (const handler of stdoutErrorHandlers) handler(err);
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
function registerStdoutErrorHandler(handler: (err: Error) => void): () => void {
|
|
332
|
-
stdoutErrorHandlers.add(handler);
|
|
333
|
-
if (!stdoutErrorListenerInstalled) {
|
|
334
|
-
process.stdout.on("error", onStdoutError);
|
|
335
|
-
stdoutErrorListenerInstalled = true;
|
|
336
|
-
}
|
|
337
|
-
return () => {
|
|
338
|
-
stdoutErrorHandlers.delete(handler);
|
|
339
|
-
};
|
|
340
|
-
}
|
|
341
322
|
|
|
342
323
|
const STD_INPUT_HANDLE = -10;
|
|
343
324
|
const ENABLE_VIRTUAL_TERMINAL_INPUT = 0x0200;
|
|
@@ -423,7 +404,7 @@ export function emergencyTerminalRestore(): void {
|
|
|
423
404
|
// Crash paths must surface subsequent stderr (fatal reports) on the
|
|
424
405
|
// real terminal; no-op when the stderr guard is inactive.
|
|
425
406
|
restoreTerminalStderr();
|
|
426
|
-
const terminal =
|
|
407
|
+
const terminal = getActiveTerminal();
|
|
427
408
|
if (terminal) {
|
|
428
409
|
// Keyboard enhancement state is screen-local: pop the alt-screen
|
|
429
410
|
// frame before leaving it, then let stop() pop zeta's main-screen frame.
|
|
@@ -527,7 +508,12 @@ export const CURSOR_SHAPE_CODES: Record<CursorShape, number> = {
|
|
|
527
508
|
bar: 6,
|
|
528
509
|
};
|
|
529
510
|
export interface Terminal {
|
|
530
|
-
|
|
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
|
+
*/
|
|
531
517
|
start(
|
|
532
518
|
onInput: (data: string) => void,
|
|
533
519
|
onResize: () => void,
|
|
@@ -539,8 +525,10 @@ export interface Terminal {
|
|
|
539
525
|
* Take ownership of stdin after a `deferInput` start: enable raw mode,
|
|
540
526
|
* attach input handlers, and run the capability probes start() skipped.
|
|
541
527
|
* Bytes the user typed in cooked mode meanwhile are replayed through
|
|
542
|
-
* `onInput`. No-op when input was never deferred.
|
|
543
|
-
*
|
|
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.
|
|
544
532
|
*/
|
|
545
533
|
enableInput?(): void;
|
|
546
534
|
|
|
@@ -672,6 +660,12 @@ export interface Terminal {
|
|
|
672
660
|
* `status` is the DECRPM value when the terminal answered DECRQM.
|
|
673
661
|
*/
|
|
674
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;
|
|
675
669
|
/**
|
|
676
670
|
* Register a callback fired once the startup Glyph Protocol handshake
|
|
677
671
|
* resolves (see {@link GlyphProtocolReportHandler}). A subscriber that
|
|
@@ -733,9 +727,18 @@ function parseOsc99KeyValues(section: string): Map<string, string> {
|
|
|
733
727
|
}
|
|
734
728
|
return values;
|
|
735
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";
|
|
736
741
|
const XTERM_SCROLL_TO_BOTTOM_MODES = [1010, 1011] as const;
|
|
737
|
-
type Osc11QueryRoute = "direct" | "tmux";
|
|
738
|
-
const TMUX_OSC11_CACHE_REFRESH_DELAY_MS = 100;
|
|
739
742
|
|
|
740
743
|
function isXtermScrollToBottomMode(mode: number): boolean {
|
|
741
744
|
return mode === 1010 || mode === 1011;
|
|
@@ -786,6 +789,9 @@ export class ProcessTerminal implements Terminal {
|
|
|
786
789
|
// Windows console fallback when kitty is unavailable: key records arrive as
|
|
787
790
|
// win32-input-mode sequences and are decoded before reaching the handler.
|
|
788
791
|
#win32InputDecoder?: Win32InputModeDecoder;
|
|
792
|
+
#win32PasteNormalizer?: Win32PasteMarkerNormalizer;
|
|
793
|
+
// Releases a relayed lone ESC the decoder held for a sequence tail that never came.
|
|
794
|
+
#win32PendingFlushTimer?: Timer;
|
|
789
795
|
#stdinBuffer?: StdinBuffer;
|
|
790
796
|
#stdinDataHandler?: (data: string) => void;
|
|
791
797
|
#disconnectHandler?: () => void;
|
|
@@ -853,10 +859,10 @@ export class ProcessTerminal implements Terminal {
|
|
|
853
859
|
#appearance: TerminalAppearance | undefined;
|
|
854
860
|
#osc11Pending = false;
|
|
855
861
|
#osc11ActiveToken?: TerminalAppearanceRequestToken;
|
|
856
|
-
#osc11QueuedQuery?: {
|
|
862
|
+
#osc11QueuedQuery?: { cache?: TerminalMultiplexerBackgroundColorCache; token?: TerminalAppearanceRequestToken };
|
|
857
863
|
#nextAppearanceRequestToken = 1;
|
|
858
864
|
#osc11ResponseBuffer = "";
|
|
859
|
-
#
|
|
865
|
+
#osc11CacheRefreshTimer?: Timer;
|
|
860
866
|
#osc99PendingId: string | undefined;
|
|
861
867
|
#osc99ResponseBuffer = "";
|
|
862
868
|
#osc99Capabilities = new Map<string, string>();
|
|
@@ -868,9 +874,15 @@ export class ProcessTerminal implements Terminal {
|
|
|
868
874
|
#glyphProtocolReplyBuffer = "";
|
|
869
875
|
#glyphProtocolResult: boolean | undefined;
|
|
870
876
|
#glyphProtocolCallbacks: GlyphProtocolReportHandler[] = [];
|
|
877
|
+
#sixelSupport: boolean | undefined;
|
|
878
|
+
#sixelSupportCallbacks: Array<(supported: boolean) => void> = [];
|
|
871
879
|
#tspPending = false;
|
|
872
880
|
#tspResult: TspHello | null | undefined;
|
|
873
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;
|
|
874
886
|
#tspReplyBuffer = "";
|
|
875
887
|
#privateCsiResponseBuffer = "";
|
|
876
888
|
#da1SentinelOwners: Da1SentinelOwner[] = [];
|
|
@@ -886,10 +898,9 @@ export class ProcessTerminal implements Terminal {
|
|
|
886
898
|
#mode2031DebounceTimer?: Timer;
|
|
887
899
|
#windowsTerminalAppearancePollTimer?: Timer;
|
|
888
900
|
#progressActive = false;
|
|
889
|
-
// Ghostty
|
|
901
|
+
// Ghostty and Monstar expire OSC 9;4 state without a heartbeat. Persistent hosts such
|
|
890
902
|
// as Windows Terminal restart their indeterminate animation on every write.
|
|
891
|
-
readonly #keepProgressAlive = TERMINAL.id === "ghostty";
|
|
892
|
-
#bracketedPasteRefreshTimer?: Timer;
|
|
903
|
+
readonly #keepProgressAlive = TERMINAL.id === "ghostty" || TERMINAL.id === "monstar";
|
|
893
904
|
#progressTimer?: Timer;
|
|
894
905
|
|
|
895
906
|
constructor(options?: ProcessTerminalOptions) {
|
|
@@ -955,10 +966,11 @@ export class ProcessTerminal implements Terminal {
|
|
|
955
966
|
|
|
956
967
|
/**
|
|
957
968
|
* Re-query the terminal background through the startup DA1-sentinel FIFO,
|
|
958
|
-
* pending/queued gating, parsing, dedup, and appearance callbacks. Inside
|
|
959
|
-
*
|
|
960
|
-
*
|
|
961
|
-
*
|
|
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
|
|
962
974
|
* no DA1 sentinel: multiplexers can decode a fragmented DA1 response as a key
|
|
963
975
|
* sequence and leak the remaining bytes into the editor. Startup and Mode 2031
|
|
964
976
|
* probes remain direct. Suppressed while inactive, headless, or after teardown.
|
|
@@ -969,7 +981,10 @@ export class ProcessTerminal implements Terminal {
|
|
|
969
981
|
if (token >= this.#nextAppearanceRequestToken) {
|
|
970
982
|
this.#nextAppearanceRequestToken = token + 1;
|
|
971
983
|
}
|
|
972
|
-
|
|
984
|
+
const cache = terminalMultiplexerSessions().find(
|
|
985
|
+
multiplexer => multiplexer.backgroundColorCache,
|
|
986
|
+
)?.backgroundColorCache;
|
|
987
|
+
this.#queryBackgroundColor(cache, token);
|
|
973
988
|
return token;
|
|
974
989
|
}
|
|
975
990
|
|
|
@@ -984,6 +999,11 @@ export class ProcessTerminal implements Terminal {
|
|
|
984
999
|
if (this.#glyphProtocolResult !== undefined) callback(this.#glyphProtocolResult);
|
|
985
1000
|
}
|
|
986
1001
|
|
|
1002
|
+
onSixelSupport(callback: (supported: boolean) => void): void {
|
|
1003
|
+
this.#sixelSupportCallbacks.push(callback);
|
|
1004
|
+
if (this.#sixelSupport !== undefined) callback(this.#sixelSupport);
|
|
1005
|
+
}
|
|
1006
|
+
|
|
987
1007
|
onTspHello(callback: TspHelloHandler): void {
|
|
988
1008
|
this.#tspCallbacks.push(callback);
|
|
989
1009
|
if (this.#tspResult !== undefined) callback(this.#tspResult);
|
|
@@ -1025,8 +1045,9 @@ export class ProcessTerminal implements Terminal {
|
|
|
1025
1045
|
if (this.#headless) return;
|
|
1026
1046
|
registerPostmortemTerminalRestore();
|
|
1027
1047
|
|
|
1028
|
-
//
|
|
1029
|
-
|
|
1048
|
+
// Own stdout: out-of-band writers route through this terminal, and the
|
|
1049
|
+
// emergency restore finds it on crash.
|
|
1050
|
+
setActiveTerminal(this);
|
|
1030
1051
|
terminalEverStarted = true;
|
|
1031
1052
|
// Own the blocking write(2) on a pump thread (unix TTYs only). A stale
|
|
1032
1053
|
// prebuilt natives module without the export falls back to direct writes.
|
|
@@ -1122,6 +1143,8 @@ export class ProcessTerminal implements Terminal {
|
|
|
1122
1143
|
// events that lose modifier information. Must run after setRawMode(true)
|
|
1123
1144
|
// since that resets console mode flags.
|
|
1124
1145
|
this.#enableWindowsVTInput();
|
|
1146
|
+
// Every response-eliciting probe goes out inside the leak guard.
|
|
1147
|
+
this.#safeWrite(PROBE_GUARD_BEGIN);
|
|
1125
1148
|
// Query and enable Kitty keyboard protocol
|
|
1126
1149
|
// The query handler intercepts input temporarily, then installs the user's handler
|
|
1127
1150
|
// See: https://sw.kovidgoyal.net/kitty/keyboard-protocol/
|
|
@@ -1184,6 +1207,7 @@ export class ProcessTerminal implements Terminal {
|
|
|
1184
1207
|
for (const mode of XTERM_SCROLL_TO_BOTTOM_MODES) {
|
|
1185
1208
|
this.#queryPrivateMode(mode);
|
|
1186
1209
|
}
|
|
1210
|
+
this.#safeWrite(PROBE_GUARD_END);
|
|
1187
1211
|
}
|
|
1188
1212
|
|
|
1189
1213
|
/**
|
|
@@ -1264,6 +1288,7 @@ export class ProcessTerminal implements Terminal {
|
|
|
1264
1288
|
|
|
1265
1289
|
// DA1 (Primary Device Attributes) response: \x1b[?...c
|
|
1266
1290
|
const da1ResponsePattern = /^\x1b\[\?[\d;]*c$/;
|
|
1291
|
+
const da1SixelAttributePattern = /;4(?:;|c$)/u;
|
|
1267
1292
|
|
|
1268
1293
|
// Private CSI partial: \x1b[?<digits/semicolons>... — incomplete probe response
|
|
1269
1294
|
// that the StdinBuffer flushed before the terminator arrived (split across
|
|
@@ -1407,6 +1432,19 @@ export class ProcessTerminal implements Terminal {
|
|
|
1407
1432
|
// outstanding sentinel — a reply that arrives after the FIFO drains (slow
|
|
1408
1433
|
// SSH/PTY links) must never reach the composer as literal text (#8542).
|
|
1409
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
|
+
}
|
|
1410
1448
|
const owner = this.#da1SentinelOwners.shift();
|
|
1411
1449
|
if (!owner) {
|
|
1412
1450
|
// Late/unowned reply: nothing to resolve, just drop the bytes.
|
|
@@ -1429,7 +1467,7 @@ export class ProcessTerminal implements Terminal {
|
|
|
1429
1467
|
) {
|
|
1430
1468
|
const query = this.#osc11QueuedQuery;
|
|
1431
1469
|
this.#osc11QueuedQuery = undefined;
|
|
1432
|
-
this.#startOsc11Query(query.
|
|
1470
|
+
this.#startOsc11Query(query.cache, query.token);
|
|
1433
1471
|
}
|
|
1434
1472
|
break;
|
|
1435
1473
|
}
|
|
@@ -1597,9 +1635,18 @@ export class ProcessTerminal implements Terminal {
|
|
|
1597
1635
|
return;
|
|
1598
1636
|
}
|
|
1599
1637
|
if (this.#inputHandler) {
|
|
1600
|
-
const
|
|
1601
|
-
|
|
1638
|
+
const decoder = this.#win32InputDecoder;
|
|
1639
|
+
const win32Keys = decoder?.decode(sequence);
|
|
1640
|
+
if (decoder && win32Keys !== undefined) {
|
|
1641
|
+
clearTimeout(this.#win32PendingFlushTimer);
|
|
1642
|
+
this.#win32PendingFlushTimer = undefined;
|
|
1602
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
|
+
}
|
|
1603
1650
|
return;
|
|
1604
1651
|
}
|
|
1605
1652
|
// Windows console hosts drop AltGr text under kitty (AltGr+F → `CSI 102;3u`);
|
|
@@ -1626,7 +1673,10 @@ export class ProcessTerminal implements Terminal {
|
|
|
1626
1673
|
|
|
1627
1674
|
// Handler that pipes stdin data through the buffer
|
|
1628
1675
|
this.#stdinDataHandler = (data: string) => {
|
|
1629
|
-
|
|
1676
|
+
// Recover modes reset by the host before the user's next paste.
|
|
1677
|
+
if (data && this.#active && this.#privateModeSupport.get(2004)) this.#safeWrite("\x1b[?2004h");
|
|
1678
|
+
if (this.#win32PasteNormalizer) this.#win32PasteNormalizer.process(data);
|
|
1679
|
+
else this.#stdinBuffer!.process(data);
|
|
1630
1680
|
};
|
|
1631
1681
|
}
|
|
1632
1682
|
|
|
@@ -1635,36 +1685,39 @@ export class ProcessTerminal implements Terminal {
|
|
|
1635
1685
|
* DA1 avoids indefinite hangs: if DA1 response arrives before OSC 11,
|
|
1636
1686
|
* the terminal does not support OSC 11.
|
|
1637
1687
|
*/
|
|
1638
|
-
#queryBackgroundColor(
|
|
1688
|
+
#queryBackgroundColor(
|
|
1689
|
+
cache?: TerminalMultiplexerBackgroundColorCache,
|
|
1690
|
+
token?: TerminalAppearanceRequestToken,
|
|
1691
|
+
): void {
|
|
1639
1692
|
if (this.#dead) return;
|
|
1640
1693
|
// Queue if an OSC 11 query is in flight or its DA1 sentinel hasn't been
|
|
1641
1694
|
// consumed yet. Starting a new query while a DA1 is outstanding would
|
|
1642
1695
|
// increment the sentinel counter, and the old DA1 arrival would then
|
|
1643
1696
|
// prematurely clear the new query's pending state. Preserve a requested
|
|
1644
|
-
//
|
|
1645
|
-
//
|
|
1697
|
+
// cache refresh when coalescing direct and explicit queries, and retain
|
|
1698
|
+
// the latest explicit request identity across automatic queries.
|
|
1646
1699
|
if (this.#osc11Pending || this.#da1SentinelOwners.some(o => o.kind === "osc11")) {
|
|
1647
1700
|
const queued = this.#osc11QueuedQuery;
|
|
1648
|
-
this.#osc11QueuedQuery = {
|
|
1649
|
-
route: queued?.route === "tmux" || route === "tmux" ? "tmux" : "direct",
|
|
1650
|
-
token: token ?? queued?.token,
|
|
1651
|
-
};
|
|
1701
|
+
this.#osc11QueuedQuery = { cache: queued?.cache ?? cache, token: token ?? queued?.token };
|
|
1652
1702
|
return;
|
|
1653
1703
|
}
|
|
1654
|
-
this.#startOsc11Query(
|
|
1704
|
+
this.#startOsc11Query(cache, token);
|
|
1655
1705
|
}
|
|
1656
1706
|
|
|
1657
|
-
#startOsc11Query(
|
|
1707
|
+
#startOsc11Query(
|
|
1708
|
+
cache: TerminalMultiplexerBackgroundColorCache | undefined,
|
|
1709
|
+
token?: TerminalAppearanceRequestToken,
|
|
1710
|
+
): void {
|
|
1658
1711
|
this.#osc11Pending = true;
|
|
1659
1712
|
this.#osc11ActiveToken = token;
|
|
1660
1713
|
this.#osc11ResponseBuffer = "";
|
|
1661
|
-
if (
|
|
1662
|
-
this.#safeWrite(
|
|
1663
|
-
this.#
|
|
1664
|
-
this.#
|
|
1714
|
+
if (cache) {
|
|
1715
|
+
this.#safeWrite(cache.passthrough("\x1b]11;?\x07"));
|
|
1716
|
+
this.#osc11CacheRefreshTimer = setTimeout(() => {
|
|
1717
|
+
this.#osc11CacheRefreshTimer = undefined;
|
|
1665
1718
|
if (this.#dead || !this.#osc11Pending) return;
|
|
1666
1719
|
this.#startDirectOsc11Query();
|
|
1667
|
-
},
|
|
1720
|
+
}, cache.settleMs);
|
|
1668
1721
|
return;
|
|
1669
1722
|
}
|
|
1670
1723
|
this.#startDirectOsc11Query();
|
|
@@ -1787,6 +1840,9 @@ export class ProcessTerminal implements Terminal {
|
|
|
1787
1840
|
#shouldQueryTspSupport(): boolean {
|
|
1788
1841
|
const override = $env.PI_TUI_NATIVE;
|
|
1789
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);
|
|
1790
1846
|
if (override === "1") return true;
|
|
1791
1847
|
// Multiplexers swallow APC, so the reply could never arrive.
|
|
1792
1848
|
if (isInsideTerminalMultiplexer($env)) return false;
|
|
@@ -1804,16 +1860,37 @@ export class ProcessTerminal implements Terminal {
|
|
|
1804
1860
|
return;
|
|
1805
1861
|
}
|
|
1806
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
|
+
}
|
|
1807
1874
|
this.#da1SentinelOwners.push({ kind: "tsp" });
|
|
1808
1875
|
this.#safeWrite(`${encodeTspHelloQuery()}\x1b[c`);
|
|
1809
1876
|
}
|
|
1810
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
|
+
|
|
1811
1887
|
#handleTspMessage(sequence: string): void {
|
|
1812
1888
|
const message = parseTspMessage(sequence);
|
|
1813
|
-
if (message?.verb === "r") {
|
|
1814
|
-
|
|
1889
|
+
if (message?.verb === "r" && message.reply.r === "hello") {
|
|
1890
|
+
this.#resolveTspSupport(message.reply);
|
|
1815
1891
|
return;
|
|
1816
1892
|
}
|
|
1893
|
+
// Events and every other reply (`blobs`) go to the native backend.
|
|
1817
1894
|
this.#inputHandler?.(sequence);
|
|
1818
1895
|
}
|
|
1819
1896
|
|
|
@@ -1822,6 +1899,12 @@ export class ProcessTerminal implements Terminal {
|
|
|
1822
1899
|
this.#tspPending = false;
|
|
1823
1900
|
const result = hello !== null && hello.v === TSP_VERSION ? hello : null;
|
|
1824
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
|
+
}
|
|
1825
1908
|
this.#tspResult = result;
|
|
1826
1909
|
for (const cb of this.#tspCallbacks) {
|
|
1827
1910
|
try {
|
|
@@ -1892,6 +1975,12 @@ export class ProcessTerminal implements Terminal {
|
|
|
1892
1975
|
// and the mode splits arrow keys into Escape plus literal text (#14034).
|
|
1893
1976
|
this.#safeWrite("\x1b[?9001h");
|
|
1894
1977
|
this.#win32InputDecoder = new Win32InputModeDecoder();
|
|
1978
|
+
// The normalizer's expiry already spent the ESC wait; release the decoder's hold
|
|
1979
|
+
// at once instead of starting a second window.
|
|
1980
|
+
this.#win32PasteNormalizer = new Win32PasteMarkerNormalizer(
|
|
1981
|
+
data => this.#stdinBuffer?.process(data),
|
|
1982
|
+
() => this.#releaseWin32PendingSequence(),
|
|
1983
|
+
);
|
|
1895
1984
|
return;
|
|
1896
1985
|
}
|
|
1897
1986
|
// A remote terminal with no identifying env may not understand the request.
|
|
@@ -1900,8 +1989,20 @@ export class ProcessTerminal implements Terminal {
|
|
|
1900
1989
|
this.#modifyOtherKeysActive = true;
|
|
1901
1990
|
}
|
|
1902
1991
|
|
|
1992
|
+
/** Deliver relayed VT text the decoder is holding (a lone ESC becomes Escape). */
|
|
1993
|
+
#releaseWin32PendingSequence(): void {
|
|
1994
|
+
clearTimeout(this.#win32PendingFlushTimer);
|
|
1995
|
+
this.#win32PendingFlushTimer = undefined;
|
|
1996
|
+
const pending = this.#win32InputDecoder?.flushPendingSequence() ?? [];
|
|
1997
|
+
for (const key of pending) this.#inputHandler?.(key);
|
|
1998
|
+
}
|
|
1999
|
+
|
|
1903
2000
|
#disableWin32InputMode(): void {
|
|
1904
2001
|
if (!this.#win32InputDecoder) return;
|
|
2002
|
+
this.#win32PasteNormalizer?.flush();
|
|
2003
|
+
this.#win32PasteNormalizer = undefined;
|
|
2004
|
+
// Held input still belongs to the user (e.g. Escape before a late kitty reply).
|
|
2005
|
+
this.#releaseWin32PendingSequence();
|
|
1905
2006
|
this.#safeWrite("\x1b[?9001l");
|
|
1906
2007
|
this.#win32InputDecoder = undefined;
|
|
1907
2008
|
}
|
|
@@ -1975,12 +2076,6 @@ export class ProcessTerminal implements Terminal {
|
|
|
1975
2076
|
// fallback resolves unsupported).
|
|
1976
2077
|
if (mode === 2004 && supported) {
|
|
1977
2078
|
this.#stdinBuffer?.setRawPasteStallProbe(this.#isLoopStalled);
|
|
1978
|
-
// A terminal can reset this mode after the initial probe (for example,
|
|
1979
|
-
// iTerm2's Terminal State toggle). Keep the mode asserted while we own
|
|
1980
|
-
// the TTY, since a stalled loop now replays unmarked bursts as keys.
|
|
1981
|
-
this.#bracketedPasteRefreshTimer ??= setInterval(() => {
|
|
1982
|
-
if (this.#active && !this.#dead) this.#safeWrite("\x1b[?2004h");
|
|
1983
|
-
}, 1000);
|
|
1984
2079
|
}
|
|
1985
2080
|
}
|
|
1986
2081
|
|
|
@@ -2114,14 +2209,10 @@ export class ProcessTerminal implements Terminal {
|
|
|
2114
2209
|
// Suppress observer/timer callbacks before any teardown can yield or throw.
|
|
2115
2210
|
this.#active = false;
|
|
2116
2211
|
this.#inputDeferred = false;
|
|
2117
|
-
if (this.#bracketedPasteRefreshTimer) {
|
|
2118
|
-
clearInterval(this.#bracketedPasteRefreshTimer);
|
|
2119
|
-
this.#bracketedPasteRefreshTimer = undefined;
|
|
2120
|
-
}
|
|
2121
2212
|
if (this.#headless) return;
|
|
2122
|
-
//
|
|
2123
|
-
if (
|
|
2124
|
-
|
|
2213
|
+
// Release stdout ownership (out-of-band writers and emergency cleanup)
|
|
2214
|
+
if (getActiveTerminal() === this) {
|
|
2215
|
+
setActiveTerminal(null);
|
|
2125
2216
|
}
|
|
2126
2217
|
|
|
2127
2218
|
// Release terminal ownership of fd 2 first so external programs,
|
|
@@ -2179,9 +2270,9 @@ export class ProcessTerminal implements Terminal {
|
|
|
2179
2270
|
clearTimeout(this.#mode2031DebounceTimer);
|
|
2180
2271
|
this.#mode2031DebounceTimer = undefined;
|
|
2181
2272
|
}
|
|
2182
|
-
if (this.#
|
|
2183
|
-
clearTimeout(this.#
|
|
2184
|
-
this.#
|
|
2273
|
+
if (this.#osc11CacheRefreshTimer) {
|
|
2274
|
+
clearTimeout(this.#osc11CacheRefreshTimer);
|
|
2275
|
+
this.#osc11CacheRefreshTimer = undefined;
|
|
2185
2276
|
}
|
|
2186
2277
|
this.#appearanceCallbacks = [];
|
|
2187
2278
|
this.#appearanceReportCallbacks = [];
|
|
@@ -2198,8 +2289,11 @@ export class ProcessTerminal implements Terminal {
|
|
|
2198
2289
|
this.#glyphProtocolResult = undefined;
|
|
2199
2290
|
this.#glyphProtocolReplyBuffer = "";
|
|
2200
2291
|
this.#glyphProtocolCallbacks = [];
|
|
2292
|
+
this.#sixelSupport = undefined;
|
|
2293
|
+
this.#sixelSupportCallbacks = [];
|
|
2201
2294
|
setTerminalGlyphProtocol(false);
|
|
2202
2295
|
this.#tspPending = false;
|
|
2296
|
+
this.#endTspTmuxPane();
|
|
2203
2297
|
this.#tspResult = undefined;
|
|
2204
2298
|
this.#tspReplyBuffer = "";
|
|
2205
2299
|
this.#tspCallbacks = [];
|
|
@@ -2289,10 +2383,6 @@ export class ProcessTerminal implements Terminal {
|
|
|
2289
2383
|
#markTerminalDisconnected(reason: string, err?: unknown): void {
|
|
2290
2384
|
if (this.#dead) return;
|
|
2291
2385
|
this.#dead = true;
|
|
2292
|
-
if (this.#bracketedPasteRefreshTimer) {
|
|
2293
|
-
clearInterval(this.#bracketedPasteRefreshTimer);
|
|
2294
|
-
this.#bracketedPasteRefreshTimer = undefined;
|
|
2295
|
-
}
|
|
2296
2386
|
this.#disarmStdoutStallWatchdog();
|
|
2297
2387
|
logger.warn("terminal disconnected; stopping interactive rendering", { reason, err });
|
|
2298
2388
|
|
|
@@ -2321,10 +2411,19 @@ export class ProcessTerminal implements Terminal {
|
|
|
2321
2411
|
}
|
|
2322
2412
|
|
|
2323
2413
|
write(data: string): void {
|
|
2414
|
+
// Hosts can reset bracketed paste (e.g. iTerm2's Terminal State toggle).
|
|
2415
|
+
// Reassert it with existing output, never on a timer while idle.
|
|
2416
|
+
if (data && this.#active && this.#privateModeSupport.get(2004)) data = `\x1b[?2004h${data}`;
|
|
2324
2417
|
this.#safeWrite(data);
|
|
2325
2418
|
if (this.#writeLogPath) {
|
|
2326
2419
|
try {
|
|
2327
|
-
|
|
2420
|
+
// Keep clipboard contents out of the debug log: record the payload's length only.
|
|
2421
|
+
const logged = data.replace(
|
|
2422
|
+
OSC52_CLIPBOARD_WRITE,
|
|
2423
|
+
(_seq, selection: string, payload: string, end: string) =>
|
|
2424
|
+
`\x1b]52;${selection};<${payload.length} bytes>${end}`,
|
|
2425
|
+
);
|
|
2426
|
+
fs.appendFileSync(this.#writeLogPath, logged, { encoding: "utf8" });
|
|
2328
2427
|
} catch {
|
|
2329
2428
|
// Ignore logging errors
|
|
2330
2429
|
}
|
|
@@ -2364,11 +2463,14 @@ export class ProcessTerminal implements Terminal {
|
|
|
2364
2463
|
this.#trackStdoutBacklog(pending);
|
|
2365
2464
|
return;
|
|
2366
2465
|
}
|
|
2367
|
-
//
|
|
2368
|
-
//
|
|
2369
|
-
|
|
2370
|
-
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");
|
|
2371
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();
|
|
2372
2474
|
// Windows ConPTY drops viewport tracking when a single write exceeds
|
|
2373
2475
|
// ~32-64 KB: the host UI's scroll position stays parked at wherever
|
|
2374
2476
|
// the write began, even though every byte landed in scrollback. Split
|
|
@@ -2397,6 +2499,8 @@ export class ProcessTerminal implements Terminal {
|
|
|
2397
2499
|
this.#trackStdoutBacklog(process.stdout.writableLength ?? 0);
|
|
2398
2500
|
} catch (err) {
|
|
2399
2501
|
this.#markTerminalDisconnected("stdout failed", err);
|
|
2502
|
+
} finally {
|
|
2503
|
+
popLoopPhase();
|
|
2400
2504
|
}
|
|
2401
2505
|
}
|
|
2402
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",
|