@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
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { encodeSixel } from "@linxiraos/pi-natives";
|
|
2
2
|
import { $env, isBunTestRuntime, isTerminalHeadless, isWsl } from "@linxiraos/pi-utils/env";
|
|
3
|
+
import { writeTerminalSequence } from "./active-terminal";
|
|
3
4
|
import { sendDesktopNotification, shouldDeliverDesktopNotification } from "./desktop-notify";
|
|
4
5
|
import {
|
|
5
6
|
detectKittyUnicodePlaceholdersSupport,
|
|
@@ -9,12 +10,17 @@ import {
|
|
|
9
10
|
renderKittyPlaceholderLines,
|
|
10
11
|
setKittyGraphics,
|
|
11
12
|
} from "./kitty-graphics";
|
|
12
|
-
import {
|
|
13
|
-
|
|
13
|
+
import {
|
|
14
|
+
isInsideTerminalMultiplexer,
|
|
15
|
+
routeTerminalMultiplexerNotification,
|
|
16
|
+
terminalMultiplexerForTerm,
|
|
17
|
+
terminalMultiplexerSessions,
|
|
18
|
+
} from "./terminal-multiplexer";
|
|
19
|
+
import { resolveTmuxClientTerminalName, wrapTmuxPassthroughIfNeeded } from "./tmux";
|
|
14
20
|
import type { HangulCompatibilityJamoWidth } from "./utils";
|
|
15
21
|
|
|
16
22
|
export * from "./terminal-multiplexer";
|
|
17
|
-
export {
|
|
23
|
+
export { wrapTmuxPassthrough } from "./tmux";
|
|
18
24
|
|
|
19
25
|
export enum ImageProtocol {
|
|
20
26
|
Kitty = "\x1b_G",
|
|
@@ -40,95 +46,10 @@ export type TerminalId =
|
|
|
40
46
|
| "otty"
|
|
41
47
|
| "rio"
|
|
42
48
|
| "tern"
|
|
49
|
+
| "monstar"
|
|
43
50
|
| "base"
|
|
44
51
|
| "trueColor";
|
|
45
52
|
|
|
46
|
-
const CMUX_NOTIFICATION_TITLE = "zeta";
|
|
47
|
-
const CMUX_SURFACE_ID_PATTERN = /^[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}$/iu;
|
|
48
|
-
|
|
49
|
-
/** Title and body for an out-of-band multiplexer notification (cmux, Herdr). */
|
|
50
|
-
function notificationTitleAndBody(message: string | TerminalNotification): { title: string; body: string } {
|
|
51
|
-
if (typeof message === "string") return { title: CMUX_NOTIFICATION_TITLE, body: message };
|
|
52
|
-
return { title: message.title?.trim() || CMUX_NOTIFICATION_TITLE, body: message.body ?? "" };
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Route a notification through cmux when the process belongs to a concrete
|
|
57
|
-
* surface. Workspace/socket state alone is not enough: only the injected
|
|
58
|
-
* surface UUID identifies the pane that should receive the notification.
|
|
59
|
-
* Returns whether cmux owns delivery so the caller can preserve every existing
|
|
60
|
-
* terminal fallback unchanged when no valid surface is present.
|
|
61
|
-
*/
|
|
62
|
-
function sendCmuxNotification(message: string | TerminalNotification, env: NodeJS.ProcessEnv = Bun.env): boolean {
|
|
63
|
-
const surfaceId = env.CMUX_SURFACE_ID?.trim();
|
|
64
|
-
if (!surfaceId || !CMUX_SURFACE_ID_PATTERN.test(surfaceId)) return false;
|
|
65
|
-
|
|
66
|
-
const { title, body } = notificationTitleAndBody(message);
|
|
67
|
-
try {
|
|
68
|
-
const child = Bun.spawn({
|
|
69
|
-
cmd: ["cmux", "notify", "--surface", surfaceId, "--title", title, "--body", body],
|
|
70
|
-
stdin: "ignore",
|
|
71
|
-
stdout: "ignore",
|
|
72
|
-
stderr: "ignore",
|
|
73
|
-
});
|
|
74
|
-
child.unref();
|
|
75
|
-
} catch {
|
|
76
|
-
// A missing cmux binary leaves delivery to the existing terminal fallback.
|
|
77
|
-
return false;
|
|
78
|
-
}
|
|
79
|
-
return true;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const HERDR_PANE_ID_PATTERN = /^[0-9A-Za-z:_-]{1,64}$/u;
|
|
83
|
-
/**
|
|
84
|
-
* `herdr notification show` takes the title as its first positional and reads
|
|
85
|
-
* exactly these three values there as a help request; it has no `--`
|
|
86
|
-
* terminator. Any other text, including one starting with `-`, is a title.
|
|
87
|
-
*/
|
|
88
|
-
const HERDR_USAGE_TOKENS = new Set(["help", "--help", "-h"]);
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Route a notification through Herdr when the process runs inside one of its
|
|
92
|
-
* panes. Herdr multiplexes panes like tmux but swallows bare OSC 9 / OSC 99 and
|
|
93
|
-
* has no DCS passthrough envelope, and its bell relay does not flag a
|
|
94
|
-
* backgrounded tab — so without this branch a backgrounded pane gets no signal
|
|
95
|
-
* at all that the agent finished or is waiting for input.
|
|
96
|
-
*
|
|
97
|
-
* `sound` maps the notification kind onto what Herdr offers: a question waiting
|
|
98
|
-
* on the user and a turn that stopped with an error both need the human and
|
|
99
|
-
* ring `request`, a settled turn rings `done`, anything else stays
|
|
100
|
-
* silent. Returns whether Herdr owns delivery, so every existing terminal
|
|
101
|
-
* fallback is preserved when the pane id is absent or the binary is missing.
|
|
102
|
-
*/
|
|
103
|
-
function sendHerdrNotification(message: string | TerminalNotification, env: NodeJS.ProcessEnv = Bun.env): boolean {
|
|
104
|
-
// Pane-only detection, like `isInsideHerdr`: an env-sanitizing launcher can
|
|
105
|
-
// drop HERDR_ENV and keep the pane identity, and that pane can still be
|
|
106
|
-
// backgrounded. The pane id itself is what the CLI needs, so it stays required.
|
|
107
|
-
if (!isInsideHerdr(env)) return false;
|
|
108
|
-
const paneId = env.HERDR_PANE_ID?.trim();
|
|
109
|
-
if (!paneId || !HERDR_PANE_ID_PATTERN.test(paneId)) return false;
|
|
110
|
-
|
|
111
|
-
const parsed = notificationTitleAndBody(message);
|
|
112
|
-
const title = HERDR_USAGE_TOKENS.has(parsed.title) ? CMUX_NOTIFICATION_TITLE : parsed.title;
|
|
113
|
-
const body = parsed.body;
|
|
114
|
-
const kinds = typeof message === "string" ? [] : [message.type ?? []].flat();
|
|
115
|
-
const sound =
|
|
116
|
-
kinds.includes("ask") || kinds.includes("error") ? "request" : kinds.includes("completion") ? "done" : "none";
|
|
117
|
-
try {
|
|
118
|
-
const child = Bun.spawn({
|
|
119
|
-
cmd: ["herdr", "notification", "show", title, "--body", body, "--sound", sound],
|
|
120
|
-
stdin: "ignore",
|
|
121
|
-
stdout: "ignore",
|
|
122
|
-
stderr: "ignore",
|
|
123
|
-
});
|
|
124
|
-
child.unref();
|
|
125
|
-
} catch {
|
|
126
|
-
// A missing herdr binary leaves delivery to the existing terminal fallback.
|
|
127
|
-
return false;
|
|
128
|
-
}
|
|
129
|
-
return true;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
53
|
const IMAGE_MARKER_SCAN_LIMIT = 512;
|
|
133
54
|
const SIXEL_MARKER_SCAN_LIMIT = 128;
|
|
134
55
|
const KITTY_PLACEHOLDER_HIGH_SURROGATE = KITTY_PLACEHOLDER.charCodeAt(0);
|
|
@@ -234,34 +155,17 @@ export class TerminalInfo {
|
|
|
234
155
|
|
|
235
156
|
sendNotification(message: string | TerminalNotification): void {
|
|
236
157
|
if (isNotificationSuppressed() || isTerminalHeadless()) return;
|
|
237
|
-
// Innermost surface first. A Herdr pane launched inside a cmux surface
|
|
238
|
-
// inherits both `HERDR_PANE_ID` and the outer `CMUX_SURFACE_ID`; routing to
|
|
239
|
-
// cmux there would flag the containing surface and leave the backgrounded
|
|
240
|
-
// Herdr pane — the one actually waiting — without a sound or a marker.
|
|
241
|
-
if (sendHerdrNotification(message)) return;
|
|
242
|
-
if (sendCmuxNotification(message)) return;
|
|
243
158
|
const formatted = this.formatNotification(message);
|
|
244
|
-
//
|
|
245
|
-
//
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
process.stdout.write(`${wrapTmuxPassthrough(formatted)}\x07`);
|
|
255
|
-
return;
|
|
256
|
-
}
|
|
257
|
-
// Zellij drops OSC 9/99 and has no DCS passthrough envelope, but raises its
|
|
258
|
-
// `[!]` bell flag on a bare BEL — the same backgrounded-pane signal tmux
|
|
259
|
-
// users get. So follow the (Zellij-swallowed) OSC with a plain BEL.
|
|
260
|
-
if (this.notifyProtocol !== NotifyProtocol.Bell && isInsideZellij()) {
|
|
261
|
-
process.stdout.write(`${formatted}\x07`);
|
|
262
|
-
return;
|
|
263
|
-
}
|
|
264
|
-
process.stdout.write(formatted);
|
|
159
|
+
// Multiplexers that swallow or rewrite the terminal's notification get it
|
|
160
|
+
// first, innermost target first; the terminal sequence is the fallback.
|
|
161
|
+
const routed = routeTerminalMultiplexerNotification({
|
|
162
|
+
message,
|
|
163
|
+
env: Bun.env,
|
|
164
|
+
sequence: this.notifyProtocol === NotifyProtocol.Bell ? null : formatted,
|
|
165
|
+
write: writeTerminalSequence,
|
|
166
|
+
});
|
|
167
|
+
if (routed) return;
|
|
168
|
+
writeTerminalSequence(formatted);
|
|
265
169
|
// VTE-family terminals (Ptyxis, GNOME Terminal, Tilix, …) plus Alacritty
|
|
266
170
|
// and bare xterm-on-Wayland have no in-band escape that surfaces an
|
|
267
171
|
// arbitrary desktop toast (#3685). When the chosen `notifyProtocol` is
|
|
@@ -274,15 +178,6 @@ export class TerminalInfo {
|
|
|
274
178
|
}
|
|
275
179
|
}
|
|
276
180
|
|
|
277
|
-
/**
|
|
278
|
-
* Whether the agent process is running inside a Zellij session. Read fresh on
|
|
279
|
-
* each call (like {@link isInsideTmux}) so a session attached/detached mid-run
|
|
280
|
-
* is observed and tests can toggle `Bun.env.ZELLIJ` per case.
|
|
281
|
-
*/
|
|
282
|
-
export function isInsideZellij(env: NodeJS.ProcessEnv = Bun.env): boolean {
|
|
283
|
-
return Boolean(env.ZELLIJ);
|
|
284
|
-
}
|
|
285
|
-
|
|
286
181
|
/**
|
|
287
182
|
* Whether the agent process runs in an SSH session, so the terminal emulator
|
|
288
183
|
* is remote and host-local input/keyboard modes cannot be assumed.
|
|
@@ -379,12 +274,12 @@ function advertisesSynchronizedOutput(termFeatures: string | undefined): boolean
|
|
|
379
274
|
* 2. Positive `TERM_FEATURES` advertisement (`Sy`) — survives SSH/mux wrapping.
|
|
380
275
|
* 3. Windows Terminal (1.24+) via `WT_SESSION`, on native win32 and the
|
|
381
276
|
* WSL/SSH-fronted host alike.
|
|
382
|
-
* 4.
|
|
383
|
-
*
|
|
384
|
-
*
|
|
385
|
-
*
|
|
386
|
-
* composite as dirty-row patches — the live viewport tears, with
|
|
387
|
-
* frozen while only the bottom refreshes.
|
|
277
|
+
* 4. A multiplexer session whose pane VTE honors DEC 2026 regardless of its
|
|
278
|
+
* DECRQM reply (Herdr's libghostty pane). Such a multiplexer still owns
|
|
279
|
+
* the grid, so leaked kitty/ghostty identities cannot enable placeholder
|
|
280
|
+
* graphics, but leaving sync off lets CUP-diff paints and split write(2)
|
|
281
|
+
* chunks composite as dirty-row patches — the live viewport tears, with
|
|
282
|
+
* the top frozen while only the bottom refreshes.
|
|
388
283
|
* 5. Known direct terminals with confirmed support. SSH does *not* disable —
|
|
389
284
|
* DEC 2026 passes through SSH when the outer terminal honors it.
|
|
390
285
|
* 6. Everything else starts off, including risky multiplexers; the runtime
|
|
@@ -400,7 +295,7 @@ export function shouldEnableSynchronizedOutputByDefault(
|
|
|
400
295
|
|
|
401
296
|
if (advertisesSynchronizedOutput(env.TERM_FEATURES)) return true;
|
|
402
297
|
if (env.WT_SESSION && (!env.TERM_PROGRAM || env.TERM_PROGRAM.toLowerCase() === "windows_terminal")) return true;
|
|
403
|
-
if (
|
|
298
|
+
if (terminalMultiplexerSessions(env).some(multiplexer => multiplexer.honorsSynchronizedOutput)) return true;
|
|
404
299
|
|
|
405
300
|
// Risky multiplexers start off even when an inner terminal id leaks through:
|
|
406
301
|
// older tmux/screen synchronized-output handling is flaky and a mux may not
|
|
@@ -413,6 +308,7 @@ export function shouldEnableSynchronizedOutputByDefault(
|
|
|
413
308
|
switch (terminalId) {
|
|
414
309
|
case "kitty":
|
|
415
310
|
case "ghostty":
|
|
311
|
+
case "monstar":
|
|
416
312
|
case "wezterm":
|
|
417
313
|
case "iterm2":
|
|
418
314
|
case "alacritty":
|
|
@@ -478,6 +374,7 @@ export function detectStyledUnderlineSupport(terminalId: TerminalId, env: NodeJS
|
|
|
478
374
|
switch (terminalId) {
|
|
479
375
|
case "kitty":
|
|
480
376
|
case "ghostty":
|
|
377
|
+
case "monstar":
|
|
481
378
|
case "wezterm":
|
|
482
379
|
return true;
|
|
483
380
|
case "iterm2": {
|
|
@@ -517,48 +414,35 @@ export function hyperlinksUserOverride(env: NodeJS.ProcessEnv = Bun.env): boolea
|
|
|
517
414
|
return null;
|
|
518
415
|
}
|
|
519
416
|
|
|
520
|
-
/**
|
|
521
|
-
* Parse tmux's self-reported version from `TERM_PROGRAM_VERSION`. tmux sets
|
|
522
|
-
* `TERM_PROGRAM=tmux` and `TERM_PROGRAM_VERSION=<version>` automatically since
|
|
523
|
-
* 3.2a; older releases (or any path that does not surface the version) yield
|
|
524
|
-
* `null` and the caller treats tmux conservatively.
|
|
525
|
-
*/
|
|
526
|
-
function parseTmuxVersionFromEnv(env: NodeJS.ProcessEnv): { major: number; minor: number } | null {
|
|
527
|
-
if (env.TERM_PROGRAM?.toLowerCase() !== "tmux") return null;
|
|
528
|
-
return parseMajorMinorVersion(env.TERM_PROGRAM_VERSION);
|
|
529
|
-
}
|
|
530
|
-
|
|
531
417
|
/**
|
|
532
418
|
* Whether OSC 8 hyperlinks should be enabled by default.
|
|
533
419
|
*
|
|
534
420
|
* Policy (highest precedence first):
|
|
535
421
|
* 1. Explicit user override (`PI_NO_HYPERLINKS=1` off, `PI_FORCE_HYPERLINKS=1`
|
|
536
422
|
* on). Opt-out wins ties.
|
|
537
|
-
* 2.
|
|
538
|
-
*
|
|
539
|
-
*
|
|
540
|
-
*
|
|
423
|
+
* 2. Every multiplexer session with a hyperlink policy renders OSC 8 itself
|
|
424
|
+
* (`render`, e.g. Herdr): on. Herdr hides the outer terminal
|
|
425
|
+
* (`TERM=xterm-256color`, no `TERM_PROGRAM`), so the outer terminal's
|
|
426
|
+
* support does not matter.
|
|
541
427
|
* 2b. Zeta workbench pane with no nested screen/tmux: on, for the same
|
|
542
428
|
* reason — the workbench's terminal simulator renders OSC 8 itself and
|
|
543
429
|
* opens links on Ctrl+click (`ZETA_WORKBENCH=1` is injected into every
|
|
544
430
|
* pane PTY; the outer terminal is invisible from inside).
|
|
545
431
|
* 3. Static terminal capability — terminals whose {@link TerminalInfo} marks
|
|
546
432
|
* `hyperlinks: false` (e.g. `base`) stay off unless the user forced on.
|
|
547
|
-
* 4.
|
|
548
|
-
*
|
|
549
|
-
* 5.
|
|
550
|
-
*
|
|
551
|
-
*
|
|
552
|
-
*
|
|
553
|
-
*
|
|
554
|
-
*
|
|
555
|
-
*
|
|
556
|
-
*
|
|
557
|
-
*
|
|
558
|
-
*
|
|
559
|
-
* 7.
|
|
560
|
-
* no version available, so off.
|
|
561
|
-
* 8. Otherwise honor the static terminal capability.
|
|
433
|
+
* 4. A session that never forwards OSC 8 (`drop`, e.g. GNU screen's `STY`)
|
|
434
|
+
* always off, even when a forwarding multiplexer is also present.
|
|
435
|
+
* 5. Forwarding sessions (`forward`, e.g. `TMUX`): enabled when every one
|
|
436
|
+
* self-reports at least its forwarding version via `TERM_PROGRAM` /
|
|
437
|
+
* `TERM_PROGRAM_VERSION` (tmux 3.4). Older or unknown versions stay off;
|
|
438
|
+
* on outer terminals without the feature configured, tmux silently drops
|
|
439
|
+
* the sequence. Checked before the TERM heuristic because tmux's
|
|
440
|
+
* historical `default-terminal` is `screen-256color`, so `TERM=screen*`
|
|
441
|
+
* inside a tmux session must NOT short-circuit to off.
|
|
442
|
+
* 6. TERM naming a multiplexer whose session markers were stripped: off,
|
|
443
|
+
* since no session proves the multiplexer forwards OSC 8 (screen never
|
|
444
|
+
* does; tmux has no version to check).
|
|
445
|
+
* 7. Otherwise honor the static terminal capability.
|
|
562
446
|
*/
|
|
563
447
|
export function shouldEnableHyperlinksByDefault(
|
|
564
448
|
env: NodeJS.ProcessEnv = Bun.env,
|
|
@@ -567,29 +451,29 @@ export function shouldEnableHyperlinksByDefault(
|
|
|
567
451
|
const override = hyperlinksUserOverride(env);
|
|
568
452
|
if (override !== null) return override;
|
|
569
453
|
|
|
570
|
-
|
|
454
|
+
const policies = terminalMultiplexerSessions(env).flatMap(multiplexer =>
|
|
455
|
+
multiplexer.hyperlinks === undefined ? [] : [multiplexer.hyperlinks],
|
|
456
|
+
);
|
|
457
|
+
if (policies.length > 0 && policies.every(policy => policy === "render")) return true;
|
|
571
458
|
|
|
572
459
|
if (isInsideZetaWorkbench(env) && !env.STY && !env.TMUX) return true;
|
|
573
460
|
|
|
574
461
|
if (!getTerminalInfo(terminalId).hyperlinks) return false;
|
|
575
462
|
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
if (
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
return version.major > 3 || (version.major === 3 && version.minor >= 4);
|
|
463
|
+
if (policies.includes("drop")) return false;
|
|
464
|
+
|
|
465
|
+
const forwarders = policies.flatMap(policy => (typeof policy === "object" ? [policy.forward] : []));
|
|
466
|
+
if (forwarders.length > 0) {
|
|
467
|
+
const termProgram = env.TERM_PROGRAM?.toLowerCase();
|
|
468
|
+
return forwarders.every(forward => {
|
|
469
|
+
if (termProgram !== forward.termProgram) return false;
|
|
470
|
+
const version = parseMajorMinorVersion(env.TERM_PROGRAM_VERSION);
|
|
471
|
+
if (!version) return false;
|
|
472
|
+
return version.major > forward.major || (version.major === forward.major && version.minor >= forward.minor);
|
|
473
|
+
});
|
|
588
474
|
}
|
|
589
475
|
|
|
590
|
-
|
|
591
|
-
if (term.startsWith("screen")) return false;
|
|
592
|
-
if (term.startsWith("tmux")) return false;
|
|
476
|
+
if (terminalMultiplexerForTerm(env)) return false;
|
|
593
477
|
|
|
594
478
|
return true;
|
|
595
479
|
}
|
|
@@ -663,10 +547,14 @@ export function resolveImageProtocol(
|
|
|
663
547
|
if (imageProtocol !== null && isPaseoEmbedder(env)) {
|
|
664
548
|
return null;
|
|
665
549
|
}
|
|
666
|
-
//
|
|
667
|
-
//
|
|
668
|
-
// can leak into the pane, so only the explicit
|
|
669
|
-
|
|
550
|
+
// Some multiplexers own the pane grid without exposing whether the attached
|
|
551
|
+
// client renders graphics (Herdr's experimental Kitty renderer). Outer-
|
|
552
|
+
// terminal identity variables can leak into the pane, so only the explicit
|
|
553
|
+
// protocol override is safe.
|
|
554
|
+
if (
|
|
555
|
+
imageProtocol !== null &&
|
|
556
|
+
terminalMultiplexerSessions(env).some(multiplexer => multiplexer.imagesRequireOverride)
|
|
557
|
+
) {
|
|
670
558
|
return null;
|
|
671
559
|
}
|
|
672
560
|
return imageProtocol;
|
|
@@ -729,6 +617,19 @@ const KNOWN_TERMINALS = Object.freeze({
|
|
|
729
617
|
// renders natively (Tern Surface Protocol) is decided by the `hello`
|
|
730
618
|
// handshake alone, never by this identity.
|
|
731
619
|
tern: new TerminalInfo("tern", ImageProtocol.Kitty, true, true, NotifyProtocol.Osc99),
|
|
620
|
+
// Monstar (rockorager/monstar) is a Wayland terminal built on libghostty. It
|
|
621
|
+
// sets TERM=monstar and answers XTVERSION with `monstar <version>`. It
|
|
622
|
+
// documents Kitty graphics with Unicode placeholders, OSC 8 hyperlinks, and
|
|
623
|
+
// synchronized output. libghostty reports Hangul Jamo as 2 cells, and the
|
|
624
|
+
// Monstar renderer draws curly underlines in the SGR 58 underline color, so
|
|
625
|
+
// the id-keyed allowlists treat Monstar like Ghostty.
|
|
626
|
+
// Monstar clears OSC 9;4 progress after 15 s without an update, so it also
|
|
627
|
+
// gets the Ghostty progress keepalive. The Ghostty initial image delay stays
|
|
628
|
+
// Ghostty-only: it works around a Ghostty app startup race, not libghostty.
|
|
629
|
+
// Monstar turns OSC 9 into a D-Bus notification with a default action;
|
|
630
|
+
// activating it focuses the Monstar window. The BEL path uses omp's own
|
|
631
|
+
// `notify-send` fallback instead, which cannot focus a window.
|
|
632
|
+
monstar: new TerminalInfo("monstar", ImageProtocol.Kitty, true, true, NotifyProtocol.Osc9, false, false, false, 2),
|
|
732
633
|
});
|
|
733
634
|
|
|
734
635
|
/** Resolve terminal identity from environment markers used by common emulators. */
|
|
@@ -750,6 +651,7 @@ export function detectTerminalId(env: NodeJS.ProcessEnv = Bun.env): TerminalId {
|
|
|
750
651
|
if (caseEq(program, "otty")) return "otty";
|
|
751
652
|
if (caseEq(program, "rio")) return "rio";
|
|
752
653
|
if (caseEq(program, "tern")) return "tern";
|
|
654
|
+
if (caseEq(program, "monstar")) return "monstar";
|
|
753
655
|
return null;
|
|
754
656
|
}
|
|
755
657
|
|
|
@@ -781,6 +683,7 @@ export function detectTerminalId(env: NodeJS.ProcessEnv = Bun.env): TerminalId {
|
|
|
781
683
|
if (clientProgramId) return clientProgramId;
|
|
782
684
|
|
|
783
685
|
if (TERM?.toLowerCase().includes("ghostty")) return "ghostty";
|
|
686
|
+
if (TERM && caseEq(TERM, "monstar")) return "monstar";
|
|
784
687
|
|
|
785
688
|
if (COLORTERM) {
|
|
786
689
|
if (caseEq(COLORTERM, "truecolor") || caseEq(COLORTERM, "24bit")) return "trueColor";
|
|
@@ -931,6 +834,13 @@ export interface ImageRenderOptions {
|
|
|
931
834
|
placementId?: number;
|
|
932
835
|
/** When true (Kitty + {@link imageId}), also return the one-time transmit sequence. */
|
|
933
836
|
includeTransmit?: boolean;
|
|
837
|
+
/**
|
|
838
|
+
* SIXEL sequence for the target pixel size renderImage computes, so the
|
|
839
|
+
* caller can encode off the JS thread: answer `undefined` while the encode
|
|
840
|
+
* is pending (the image's rows stay reserved) and `null` once it failed.
|
|
841
|
+
* Without a provider, renderImage encodes synchronously.
|
|
842
|
+
*/
|
|
843
|
+
sixel?: (widthPx: number, heightPx: number) => string | null | undefined;
|
|
934
844
|
}
|
|
935
845
|
|
|
936
846
|
// Default cell dimensions - updated by TUI when terminal responds to query
|
|
@@ -1024,8 +934,7 @@ export function encodeKittyPlacement(options: {
|
|
|
1024
934
|
/**
|
|
1025
935
|
* Exact shape of the direct-placement line {@link Image} emits as its block's
|
|
1026
936
|
* last row: optional `ESC 7` + `CUU(rows-1)` prefix, the {@link encodeKittyPlacement}
|
|
1027
|
-
* APC, optional `ESC 8` suffix. tmux
|
|
1028
|
-
* not match (passthrough placements stay untouched).
|
|
937
|
+
* APC, optional `ESC 8` suffix. The parser unwraps tmux passthrough first.
|
|
1029
938
|
*/
|
|
1030
939
|
const KITTY_DIRECT_PLACEMENT_LINE =
|
|
1031
940
|
/^(?:\x1b7(?:\x1b\[(\d+)A)?)?\x1b_Ga=p,q=2,C=1,i=(\d+)(?:,p=(\d+))?(?:,c=(\d+))?(?:,r=(\d+))?\x1b\\(?:\x1b8)?$/;
|
|
@@ -1040,10 +949,15 @@ export interface ParsedKittyPlacementLine {
|
|
|
1040
949
|
/**
|
|
1041
950
|
* Parse a frame line that consists solely of a Kitty direct placement (the
|
|
1042
951
|
* last line of an {@link Image} block). Returns null for anything else —
|
|
1043
|
-
* placeholder grids,
|
|
952
|
+
* placeholder grids, sixel/iTerm2 payloads — so
|
|
1044
953
|
* callers fall back to writing the line verbatim.
|
|
1045
954
|
*/
|
|
1046
955
|
export function parseKittyDirectPlacementLine(line: string): ParsedKittyPlacementLine | null {
|
|
956
|
+
if (line.includes("\x1bPtmux;")) {
|
|
957
|
+
line = line.replace(/\x1bPtmux;((?:[^\x1b]|\x1b\x1b)*)\x1b\\/g, (_match, payload: string) =>
|
|
958
|
+
payload.replaceAll("\x1b\x1b", "\x1b"),
|
|
959
|
+
);
|
|
960
|
+
}
|
|
1047
961
|
const m = KITTY_DIRECT_PLACEMENT_LINE.exec(line);
|
|
1048
962
|
if (!m) return null;
|
|
1049
963
|
const columns = m[4] !== undefined ? Number(m[4]) : 0;
|
|
@@ -1088,9 +1002,7 @@ export function encodeKittyPlacementLine(options: {
|
|
|
1088
1002
|
const srcY = Math.floor((options.imageHeightPx * hiddenRows) / options.rows);
|
|
1089
1003
|
params.push(`y=${srcY}`, `h=${Math.max(1, options.imageHeightPx - srcY)}`);
|
|
1090
1004
|
}
|
|
1091
|
-
|
|
1092
|
-
// wrapped, never parses, and never reaches this rewrite.
|
|
1093
|
-
const apc = `\x1b_G${params.join(",")}\x1b\\`;
|
|
1005
|
+
const apc = wrapTmuxPassthroughIfNeeded(`\x1b_G${params.join(",")}\x1b\\`);
|
|
1094
1006
|
const cuu = visibleRows - 1;
|
|
1095
1007
|
return cuu > 0 ? `\x1b7\x1b[${cuu}A${apc}\x1b8` : apc;
|
|
1096
1008
|
}
|
|
@@ -1117,8 +1029,8 @@ export function encodeKittyDeleteAllImages(): string {
|
|
|
1117
1029
|
/**
|
|
1118
1030
|
* Delete a single placement of an image (`d=i`, lowercase): removes its cells
|
|
1119
1031
|
* and registry entry but keeps the transmitted data, so a later `a=p` under a
|
|
1120
|
-
* fresh placement id needs no retransmit.
|
|
1121
|
-
*
|
|
1032
|
+
* fresh placement id needs no retransmit. The frame writer uses this before
|
|
1033
|
+
* replacing a live image without deleting its archived copies.
|
|
1122
1034
|
*/
|
|
1123
1035
|
export function encodeKittyDeletePlacement(imageId: number, placementId: number): string {
|
|
1124
1036
|
return wrapTmuxPassthroughIfNeeded(`\x1b_Ga=d,d=i,i=${imageId},p=${placementId},q=2\x1b\\`);
|
|
@@ -1331,6 +1243,19 @@ export function getImageDimensions(base64Data: string, mimeType: string): ImageD
|
|
|
1331
1243
|
return null;
|
|
1332
1244
|
}
|
|
1333
1245
|
|
|
1246
|
+
/**
|
|
1247
|
+
* SIXEL sequence for `base64Data` at the given pixel size, encoded on the JS
|
|
1248
|
+
* thread; `null` when the encode fails. For output that cannot wait for an
|
|
1249
|
+
* off-thread encode.
|
|
1250
|
+
*/
|
|
1251
|
+
export function encodeSixelNow(base64Data: string, widthPx: number, heightPx: number): string | null {
|
|
1252
|
+
try {
|
|
1253
|
+
return encodeSixel(new Uint8Array(Buffer.from(base64Data, "base64")), widthPx, heightPx);
|
|
1254
|
+
} catch {
|
|
1255
|
+
return null;
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1334
1259
|
export function renderImage(
|
|
1335
1260
|
base64Data: string,
|
|
1336
1261
|
imageDimensions: ImageDimensions,
|
|
@@ -1384,29 +1309,28 @@ export function renderImage(
|
|
|
1384
1309
|
}
|
|
1385
1310
|
|
|
1386
1311
|
if (TERMINAL.imageProtocol === ImageProtocol.Sixel) {
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
}
|
|
1312
|
+
// SIXEL encodes in 6-pixel vertical bands. A height that is not a
|
|
1313
|
+
// multiple of 6 is padded with transparent rows, but the terminal
|
|
1314
|
+
// still allocates cell rows for the padded height. When the padded
|
|
1315
|
+
// height crosses a cell boundary the terminal uses one more row
|
|
1316
|
+
// than fit.rows, so the next line of content overwrites the bottom
|
|
1317
|
+
// of the image — a visible slice stripped from the image. Round the
|
|
1318
|
+
// encode height DOWN to the largest multiple of 6 that fits within
|
|
1319
|
+
// the requested row budget, so the band boundary aligns without
|
|
1320
|
+
// padding and the reserved row count never exceeds fit.rows. Scale
|
|
1321
|
+
// the width by the same ratio so resize_exact preserves the aspect
|
|
1322
|
+
// ratio instead of squashing the image vertically.
|
|
1323
|
+
const rawHeightPx = Math.max(1, fit.rows * cellDims.heightPx);
|
|
1324
|
+
const targetHeightPx = Math.max(6, Math.floor(rawHeightPx / 6) * 6);
|
|
1325
|
+
const heightScale = targetHeightPx / rawHeightPx;
|
|
1326
|
+
const targetWidthPx = Math.max(1, Math.round(fit.columns * cellDims.widthPx * heightScale));
|
|
1327
|
+
const rows = Math.max(1, Math.ceil(targetHeightPx / cellDims.heightPx));
|
|
1328
|
+
const sequence = options.sixel
|
|
1329
|
+
? options.sixel(targetWidthPx, targetHeightPx)
|
|
1330
|
+
: encodeSixelNow(base64Data, targetWidthPx, targetHeightPx);
|
|
1331
|
+
if (sequence === null) return null;
|
|
1332
|
+
// Undefined while the provider's encode is pending: the rows stay reserved.
|
|
1333
|
+
return { sequence, rows };
|
|
1410
1334
|
}
|
|
1411
1335
|
if (TERMINAL.imageProtocol === ImageProtocol.Iterm2) {
|
|
1412
1336
|
const sequence = encodeITerm2(base64Data, {
|