@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/components/form.ts
CHANGED
|
@@ -97,6 +97,10 @@ class OptionalText implements Component {
|
|
|
97
97
|
this.#text = new StyledText("", style, token);
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
+
get value(): string {
|
|
101
|
+
return this.#value;
|
|
102
|
+
}
|
|
103
|
+
|
|
100
104
|
describe(cx: DescribeContext): NativeNode {
|
|
101
105
|
return this.#value ? this.#text.describe(cx) : this.#hidden;
|
|
102
106
|
}
|
|
@@ -200,6 +204,10 @@ export class FormField implements Component, Focusable, MouseRoutable {
|
|
|
200
204
|
return this.#focused;
|
|
201
205
|
}
|
|
202
206
|
|
|
207
|
+
get hasError(): boolean {
|
|
208
|
+
return this.#error.value !== "";
|
|
209
|
+
}
|
|
210
|
+
|
|
203
211
|
get debugChildren(): readonly Component[] {
|
|
204
212
|
return [this.control];
|
|
205
213
|
}
|
|
@@ -16,7 +16,21 @@ export interface ImageOptions {
|
|
|
16
16
|
* repaint replaces the placement instead of stacking a duplicate.
|
|
17
17
|
*/
|
|
18
18
|
imageKey?: string;
|
|
19
|
+
/**
|
|
20
|
+
* Schedules a repaint once this image's off-thread SIXEL encode lands.
|
|
21
|
+
* Defaults to the budget's repaint; a host without a budget must pass one,
|
|
22
|
+
* or the image's reserved rows stay blank until an unrelated repaint.
|
|
23
|
+
*/
|
|
24
|
+
requestRender?: () => void;
|
|
19
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* Run `render` for rows bound for native scrollback. Those rows are never
|
|
28
|
+
* repainted, so an image whose off-thread SIXEL encode has not landed encodes
|
|
29
|
+
* synchronously instead of committing its reserved blank rows.
|
|
30
|
+
*/
|
|
31
|
+
export declare function renderForScrollback<T>(render: () => T): T;
|
|
32
|
+
/** Widest an {@link Image} rendered at `width` columns draws; callers sizing a raster for it (`SvgFigure`) match it. */
|
|
33
|
+
export declare function imageMaxColumns(width: number): number;
|
|
20
34
|
/** Default count of inline images kept as live graphics before older ones fall back to text. */
|
|
21
35
|
export declare const DEFAULT_MAX_INLINE_IMAGES = 8;
|
|
22
36
|
/**
|
|
@@ -52,6 +66,8 @@ export declare class ImageBudget {
|
|
|
52
66
|
get cap(): number;
|
|
53
67
|
get enabled(): boolean;
|
|
54
68
|
setRequestRender(requestRender: () => void): void;
|
|
69
|
+
/** Ask for a repaint, e.g. once an image's off-thread encode settles. */
|
|
70
|
+
requestRender(): void;
|
|
55
71
|
setCap(cap: number): void;
|
|
56
72
|
/**
|
|
57
73
|
* Stable graphics id for a logical image. A non-empty `key` maps to the same
|
|
@@ -59,6 +75,13 @@ export declare class ImageBudget {
|
|
|
59
75
|
* gets a fresh id every call.
|
|
60
76
|
*/
|
|
61
77
|
acquireId(key?: string): number;
|
|
78
|
+
/**
|
|
79
|
+
* Retire the graphic held under `key` as soon as no frame shows it, instead
|
|
80
|
+
* of when the residency sweep reaches it. For an owner that replaced the
|
|
81
|
+
* image for good — a streaming SVG figure's earlier raster — so superseded
|
|
82
|
+
* revisions never push older images (scrollback included) out of the store.
|
|
83
|
+
*/
|
|
84
|
+
release(key: string): void;
|
|
62
85
|
/**
|
|
63
86
|
* Start an alternate-buffer lifecycle. Call once per `?1049h`, before the
|
|
64
87
|
* first pass of the fullscreen overlay or resize borrow that owns the buffer.
|
|
@@ -126,50 +149,8 @@ export declare class ImageBudget {
|
|
|
126
149
|
* the image is purged from the terminal store.
|
|
127
150
|
*/
|
|
128
151
|
registerPlacementGeometry(imageId: number, widthPx: number, heightPx: number): void;
|
|
129
|
-
/**
|
|
130
|
-
|
|
131
|
-
* that is committed once the frame's writes land). Called once per rendered
|
|
132
|
-
* frame — including frames that emit no placements — so an epoch whose rows
|
|
133
|
-
* commit while its line is never rewritten is still flagged before the next
|
|
134
|
-
* re-emission.
|
|
135
|
-
*/
|
|
136
|
-
observeCommitWatermark(committedTo: number): void;
|
|
137
|
-
/**
|
|
138
|
-
* End the physical-row coordinate epoch after observing its final commit
|
|
139
|
-
* watermark. Placement ids and latched archive state survive, but attachment
|
|
140
|
-
* rows do not: the next placement emit records them in the new-width frame.
|
|
141
|
-
*/
|
|
142
|
-
beginPlacementCoordinateEpoch(): void;
|
|
143
|
-
/**
|
|
144
|
-
* Resolve the placement id and geometry for a direct-placement emit whose
|
|
145
|
-
* topmost attached cell sits at `attachTopFrameRow` — the first frame row
|
|
146
|
-
* the placement covers, i.e. the block's first *visible* row, not its
|
|
147
|
-
* origin (-1 when the writer has no frame-space position: alt-screen,
|
|
148
|
-
* resize, ConPTY-truncated replays). `committedTo` is this frame's commit
|
|
149
|
-
* target in the same frame-row space (-1 when unknown).
|
|
150
|
-
*
|
|
151
|
-
* Invariant: a placement id may be re-used (Kitty replace strips that id's
|
|
152
|
-
* cells everywhere, scrollback included) only while none of the cells it
|
|
153
|
-
* attached have entered native scrollback. The epoch — the `p=` id —
|
|
154
|
-
* advances exactly when the archived flag says otherwise; rewrites with no
|
|
155
|
-
* commit progression keep replacing the same id in place.
|
|
156
|
-
*/
|
|
157
|
-
resolvePlacementEmit(imageId: number, attachTopFrameRow: number, committedTo: number): {
|
|
158
|
-
placementId: number;
|
|
159
|
-
widthPx: number;
|
|
160
|
-
heightPx: number;
|
|
161
|
-
} | null;
|
|
162
|
-
/**
|
|
163
|
-
* Restart every placement epoch after a destructive history clear (`CSI 3 J`
|
|
164
|
-
* full paint). The clear destroys all placement cells — scrollback rows are
|
|
165
|
-
* gone and the replay rewrites the viewport — so no archive remains to
|
|
166
|
-
* protect. Reverting to epoch 1 lets the replay recreate every visible
|
|
167
|
-
* placement after the terminal-wide cleanup.
|
|
168
|
-
*/
|
|
169
|
-
resetPlacementEpochs(): ReadonlyArray<{
|
|
170
|
-
imageId: number;
|
|
171
|
-
lastEpoch: number;
|
|
172
|
-
}>;
|
|
152
|
+
/** Source dimensions used by the frame writer to clip a direct placement. */
|
|
153
|
+
getPlacementGeometry(imageId: number): ImageDimensions | undefined;
|
|
173
154
|
/**
|
|
174
155
|
* Queue a one-time transmit for `imageId` on the surface the in-flight pass
|
|
175
156
|
* paints. No-op if that surface already holds the data, so a repeated call
|
|
@@ -204,6 +185,7 @@ export declare class Image implements Component {
|
|
|
204
185
|
/** Return source metadata without exposing the encoded image buffer. */
|
|
205
186
|
debugState(): Record<string, unknown>;
|
|
206
187
|
invalidate(): void;
|
|
188
|
+
releaseRenderCaches(): void;
|
|
207
189
|
/**
|
|
208
190
|
* A native `image` backed by a content-addressed blob; the terminal fits
|
|
209
191
|
* it. Cell caps become `ch`/`lines` bounds. The inline-image budget and
|
package/src/components/image.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { encodeSixelAsync } from "@linxiraos/pi-natives";
|
|
1
2
|
import { getKittyGraphics } from "../kitty-graphics";
|
|
2
3
|
import {
|
|
4
|
+
encodeSixelNow,
|
|
3
5
|
getCellDimensions,
|
|
4
6
|
getImageDimensions,
|
|
5
7
|
type ImageDimensions,
|
|
@@ -8,8 +10,7 @@ import {
|
|
|
8
10
|
renderImage,
|
|
9
11
|
TERMINAL,
|
|
10
12
|
} from "../terminal-capabilities";
|
|
11
|
-
import {
|
|
12
|
-
import { node } from "../native/describe";
|
|
13
|
+
import { nativeImageNode } from "../native/blobs";
|
|
13
14
|
import type { DescribeContext, NativeNode } from "../native/node";
|
|
14
15
|
import type { Component } from "../tui";
|
|
15
16
|
|
|
@@ -29,39 +30,47 @@ export interface ImageOptions {
|
|
|
29
30
|
* repaint replaces the placement instead of stacking a duplicate.
|
|
30
31
|
*/
|
|
31
32
|
imageKey?: string;
|
|
33
|
+
/**
|
|
34
|
+
* Schedules a repaint once this image's off-thread SIXEL encode lands.
|
|
35
|
+
* Defaults to the budget's repaint; a host without a budget must pass one,
|
|
36
|
+
* or the image's reserved rows stay blank until an unrelated repaint.
|
|
37
|
+
*/
|
|
38
|
+
requestRender?: () => void;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Renders in progress whose rows are bound for native scrollback. */
|
|
42
|
+
let scrollbackRenderDepth = 0;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Run `render` for rows bound for native scrollback. Those rows are never
|
|
46
|
+
* repainted, so an image whose off-thread SIXEL encode has not landed encodes
|
|
47
|
+
* synchronously instead of committing its reserved blank rows.
|
|
48
|
+
*/
|
|
49
|
+
export function renderForScrollback<T>(render: () => T): T {
|
|
50
|
+
scrollbackRenderDepth++;
|
|
51
|
+
try {
|
|
52
|
+
return render();
|
|
53
|
+
} finally {
|
|
54
|
+
scrollbackRenderDepth--;
|
|
55
|
+
}
|
|
32
56
|
}
|
|
33
57
|
|
|
34
58
|
const EMPTY_IDS: readonly number[] = [];
|
|
35
59
|
const EMPTY_TRANSMITS: readonly string[] = [];
|
|
36
|
-
const EMPTY_STALE_EPOCHS: ReadonlyArray<{ imageId: number; lastEpoch: number }> = [];
|
|
37
60
|
const SAVE_CURSOR = "\x1b7";
|
|
38
61
|
const RESTORE_CURSOR = "\x1b8";
|
|
39
62
|
// Direct placements reserve height with leading zero-width rows. Keep them
|
|
40
63
|
// non-plain so transcript blank-edge trimming does not collapse image-only blocks.
|
|
41
64
|
const RESERVED_IMAGE_ROW = "\x1b[0m";
|
|
42
65
|
|
|
66
|
+
/** Widest an {@link Image} rendered at `width` columns draws; callers sizing a raster for it (`SvgFigure`) match it. */
|
|
67
|
+
export function imageMaxColumns(width: number): number {
|
|
68
|
+
return Math.max(1, width - 2);
|
|
69
|
+
}
|
|
70
|
+
|
|
43
71
|
/** Default count of inline images kept as live graphics before older ones fall back to text. */
|
|
44
72
|
export const DEFAULT_MAX_INLINE_IMAGES = 8;
|
|
45
73
|
|
|
46
|
-
/** Per-image direct-placement emit state tracked by {@link ImageBudget}. */
|
|
47
|
-
interface PlacementEmitState {
|
|
48
|
-
widthPx: number;
|
|
49
|
-
heightPx: number;
|
|
50
|
-
/** Current placement-id (`p=`) generation. */
|
|
51
|
-
epoch: number;
|
|
52
|
-
/** First frame row the current epoch's last emit attached cells to. */
|
|
53
|
-
lastAttachTopFrameRow: number | undefined;
|
|
54
|
-
/**
|
|
55
|
-
* Whether any cell attached by the current epoch's last emit has entered
|
|
56
|
-
* native scrollback. Set by {@link ImageBudget.observeCommitWatermark}
|
|
57
|
-
* comparing each frame's raw commit target against the attach top —
|
|
58
|
-
* era-local comparisons, so a divergence recommit that rewinds and
|
|
59
|
-
* re-advances the ledger is detected the moment it re-crosses the attach
|
|
60
|
-
* top, and a stale pre-rewind peak can never re-trigger.
|
|
61
|
-
*/
|
|
62
|
-
cellsArchived: boolean;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
74
|
/** A surface the renderer paints frames on. */
|
|
66
75
|
type Surface = "screen" | "alt";
|
|
67
76
|
const SURFACES: readonly Surface[] = ["screen", "alt"];
|
|
@@ -193,22 +202,10 @@ export class ImageBudget {
|
|
|
193
202
|
* fullscreen overlay keeps its placements and is restored from cache on exit.
|
|
194
203
|
*/
|
|
195
204
|
#liveIds: Record<Surface, Set<number>> = { screen: new Set(), alt: new Set() };
|
|
196
|
-
/**
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
* terminal's own placement registry for the image — they are the ledger the
|
|
201
|
-
* destructive-clear sweep uses to delete every registry entry an image ever
|
|
202
|
-
* placed — and die with it on demotion purge (`d=I`) or full cleanup.
|
|
203
|
-
*/
|
|
204
|
-
#placementState = new Map<number, PlacementEmitState>();
|
|
205
|
-
/**
|
|
206
|
-
* States with an un-archived live attach top — the only ones a frame's
|
|
207
|
-
* commit watermark can affect. {@link observeCommitWatermark} runs every
|
|
208
|
-
* rendered frame, so it scans this set (bounded by concurrently live
|
|
209
|
-
* placements) instead of every image ever registered.
|
|
210
|
-
*/
|
|
211
|
-
#watchedPlacements = new Set<PlacementEmitState>();
|
|
205
|
+
/** Source pixel dimensions for clipping direct placements at the viewport edge. */
|
|
206
|
+
#placementGeometry = new Map<number, ImageDimensions>();
|
|
207
|
+
/** Ids whose owner replaced them for good ({@link release}); retired once no frame shows them. */
|
|
208
|
+
#released = new Set<number>();
|
|
212
209
|
|
|
213
210
|
constructor(cap: number = DEFAULT_MAX_INLINE_IMAGES, requestRender: () => void = () => {}) {
|
|
214
211
|
this.#cap = normalizeCap(cap);
|
|
@@ -227,6 +224,11 @@ export class ImageBudget {
|
|
|
227
224
|
this.#requestRender = requestRender;
|
|
228
225
|
}
|
|
229
226
|
|
|
227
|
+
/** Ask for a repaint, e.g. once an image's off-thread encode settles. */
|
|
228
|
+
requestRender(): void {
|
|
229
|
+
this.#requestRender();
|
|
230
|
+
}
|
|
231
|
+
|
|
230
232
|
setCap(cap: number): void {
|
|
231
233
|
const next = normalizeCap(cap);
|
|
232
234
|
if (next === this.#cap) return;
|
|
@@ -254,6 +256,17 @@ export class ImageBudget {
|
|
|
254
256
|
return id;
|
|
255
257
|
}
|
|
256
258
|
|
|
259
|
+
/**
|
|
260
|
+
* Retire the graphic held under `key` as soon as no frame shows it, instead
|
|
261
|
+
* of when the residency sweep reaches it. For an owner that replaced the
|
|
262
|
+
* image for good — a streaming SVG figure's earlier raster — so superseded
|
|
263
|
+
* revisions never push older images (scrollback included) out of the store.
|
|
264
|
+
*/
|
|
265
|
+
release(key: string): void {
|
|
266
|
+
const id = this.#keyToId.get(key);
|
|
267
|
+
if (id !== undefined) this.#released.add(id);
|
|
268
|
+
}
|
|
269
|
+
|
|
257
270
|
/**
|
|
258
271
|
* Start an alternate-buffer lifecycle. Call once per `?1049h`, before the
|
|
259
272
|
* first pass of the fullscreen overlay or resize borrow that owns the buffer.
|
|
@@ -375,6 +388,13 @@ export class ImageBudget {
|
|
|
375
388
|
if (this.#passShowsLive(id)) liveIds.add(id);
|
|
376
389
|
}
|
|
377
390
|
const transmitted = this.#transmitted[this.#surface];
|
|
391
|
+
for (const id of this.#released) {
|
|
392
|
+
// #retire refuses while this pass or the other surface still shows it.
|
|
393
|
+
if (transmitted.has(id)) this.#retire(id);
|
|
394
|
+
if (this.#isTransmitted(id)) continue;
|
|
395
|
+
this.#forgetKeyForId(id);
|
|
396
|
+
this.#released.delete(id);
|
|
397
|
+
}
|
|
378
398
|
if (this.#cap <= 0 || transmitted.size <= this.#cap) return;
|
|
379
399
|
for (const id of transmitted) {
|
|
380
400
|
if (transmitted.size <= this.#cap) break;
|
|
@@ -426,7 +446,7 @@ export class ImageBudget {
|
|
|
426
446
|
// terminal, so cancel it instead of transmitting then purging.
|
|
427
447
|
if (!this.#pendingTransmits[this.#surface].delete(imageId)) this.#purgeIds.push(imageId);
|
|
428
448
|
this.#transmitted[this.#surface].delete(imageId);
|
|
429
|
-
if (!this.#isTransmitted(imageId)) this.#
|
|
449
|
+
if (!this.#isTransmitted(imageId)) this.#placementGeometry.delete(imageId);
|
|
430
450
|
this.#forgetKeyForId(imageId);
|
|
431
451
|
return true;
|
|
432
452
|
}
|
|
@@ -469,8 +489,8 @@ export class ImageBudget {
|
|
|
469
489
|
this.#resetPurgeIds = [];
|
|
470
490
|
this.#keyToId.clear();
|
|
471
491
|
this.#idToKey.clear();
|
|
472
|
-
this.#
|
|
473
|
-
this.#
|
|
492
|
+
this.#placementGeometry.clear();
|
|
493
|
+
this.#released.clear();
|
|
474
494
|
for (const surface of SURFACES) this.#liveIds[surface].clear();
|
|
475
495
|
return [...ids];
|
|
476
496
|
}
|
|
@@ -486,115 +506,18 @@ export class ImageBudget {
|
|
|
486
506
|
* the image is purged from the terminal store.
|
|
487
507
|
*/
|
|
488
508
|
registerPlacementGeometry(imageId: number, widthPx: number, heightPx: number): void {
|
|
489
|
-
const
|
|
490
|
-
if (
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
this.#placementState.set(imageId, {
|
|
496
|
-
widthPx,
|
|
497
|
-
heightPx,
|
|
498
|
-
epoch: 1,
|
|
499
|
-
lastAttachTopFrameRow: undefined,
|
|
500
|
-
cellsArchived: false,
|
|
501
|
-
});
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
/**
|
|
505
|
-
* Record this frame's native-scrollback commit target (the frame-row count
|
|
506
|
-
* that is committed once the frame's writes land). Called once per rendered
|
|
507
|
-
* frame — including frames that emit no placements — so an epoch whose rows
|
|
508
|
-
* commit while its line is never rewritten is still flagged before the next
|
|
509
|
-
* re-emission.
|
|
510
|
-
*/
|
|
511
|
-
observeCommitWatermark(committedTo: number): void {
|
|
512
|
-
if (committedTo < 0 || this.#watchedPlacements.size === 0) return;
|
|
513
|
-
for (const state of this.#watchedPlacements) {
|
|
514
|
-
if (state.lastAttachTopFrameRow !== undefined && committedTo > state.lastAttachTopFrameRow) {
|
|
515
|
-
// Latched: the flag only clears when the next emit consumes it,
|
|
516
|
-
// so the state needs no further per-frame scans until then.
|
|
517
|
-
state.cellsArchived = true;
|
|
518
|
-
this.#watchedPlacements.delete(state);
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
/**
|
|
524
|
-
* End the physical-row coordinate epoch after observing its final commit
|
|
525
|
-
* watermark. Placement ids and latched archive state survive, but attachment
|
|
526
|
-
* rows do not: the next placement emit records them in the new-width frame.
|
|
527
|
-
*/
|
|
528
|
-
beginPlacementCoordinateEpoch(): void {
|
|
529
|
-
for (const state of this.#placementState.values()) state.lastAttachTopFrameRow = undefined;
|
|
530
|
-
this.#watchedPlacements.clear();
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
/**
|
|
534
|
-
* Resolve the placement id and geometry for a direct-placement emit whose
|
|
535
|
-
* topmost attached cell sits at `attachTopFrameRow` — the first frame row
|
|
536
|
-
* the placement covers, i.e. the block's first *visible* row, not its
|
|
537
|
-
* origin (-1 when the writer has no frame-space position: alt-screen,
|
|
538
|
-
* resize, ConPTY-truncated replays). `committedTo` is this frame's commit
|
|
539
|
-
* target in the same frame-row space (-1 when unknown).
|
|
540
|
-
*
|
|
541
|
-
* Invariant: a placement id may be re-used (Kitty replace strips that id's
|
|
542
|
-
* cells everywhere, scrollback included) only while none of the cells it
|
|
543
|
-
* attached have entered native scrollback. The epoch — the `p=` id —
|
|
544
|
-
* advances exactly when the archived flag says otherwise; rewrites with no
|
|
545
|
-
* commit progression keep replacing the same id in place.
|
|
546
|
-
*/
|
|
547
|
-
resolvePlacementEmit(
|
|
548
|
-
imageId: number,
|
|
549
|
-
attachTopFrameRow: number,
|
|
550
|
-
committedTo: number,
|
|
551
|
-
): { placementId: number; widthPx: number; heightPx: number } | null {
|
|
552
|
-
const state = this.#placementState.get(imageId);
|
|
553
|
-
if (!state) return null;
|
|
554
|
-
// Frames that commit as they write (seam/full-paint chunk passes) pass
|
|
555
|
-
// their own commit target; fold it in before deciding, so a commit that
|
|
556
|
-
// lands in the same frame as the re-emission still advances the epoch.
|
|
557
|
-
if (committedTo >= 0 && state.lastAttachTopFrameRow !== undefined && committedTo > state.lastAttachTopFrameRow) {
|
|
558
|
-
state.cellsArchived = true;
|
|
559
|
-
this.#watchedPlacements.delete(state);
|
|
560
|
-
}
|
|
561
|
-
if (state.cellsArchived) {
|
|
562
|
-
state.epoch += 1;
|
|
563
|
-
state.cellsArchived = false;
|
|
564
|
-
state.lastAttachTopFrameRow = undefined;
|
|
565
|
-
}
|
|
566
|
-
if (attachTopFrameRow >= 0) {
|
|
567
|
-
state.lastAttachTopFrameRow = attachTopFrameRow;
|
|
568
|
-
this.#watchedPlacements.add(state);
|
|
569
|
-
}
|
|
570
|
-
return { placementId: state.epoch, widthPx: state.widthPx, heightPx: state.heightPx };
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
/**
|
|
574
|
-
* Restart every placement epoch after a destructive history clear (`CSI 3 J`
|
|
575
|
-
* full paint). The clear destroys all placement cells — scrollback rows are
|
|
576
|
-
* gone and the replay rewrites the viewport — so no archive remains to
|
|
577
|
-
* protect. Reverting to epoch 1 lets the replay recreate every visible
|
|
578
|
-
* placement after the terminal-wide cleanup.
|
|
579
|
-
*/
|
|
580
|
-
resetPlacementEpochs(): ReadonlyArray<{ imageId: number; lastEpoch: number }> {
|
|
581
|
-
let stale: Array<{ imageId: number; lastEpoch: number }> | undefined;
|
|
582
|
-
for (const [imageId, state] of this.#placementState) {
|
|
583
|
-
stale ??= [];
|
|
584
|
-
stale.push({ imageId, lastEpoch: state.epoch });
|
|
585
|
-
state.epoch = 1;
|
|
586
|
-
state.lastAttachTopFrameRow = undefined;
|
|
587
|
-
state.cellsArchived = false;
|
|
509
|
+
const geometry = this.#placementGeometry.get(imageId);
|
|
510
|
+
if (geometry) {
|
|
511
|
+
geometry.widthPx = widthPx;
|
|
512
|
+
geometry.heightPx = heightPx;
|
|
513
|
+
} else {
|
|
514
|
+
this.#placementGeometry.set(imageId, { widthPx, heightPx });
|
|
588
515
|
}
|
|
589
|
-
this.#watchedPlacements.clear();
|
|
590
|
-
return stale ?? EMPTY_STALE_EPOCHS;
|
|
591
516
|
}
|
|
592
517
|
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
this.#watchedPlacements.delete(state);
|
|
597
|
-
this.#placementState.delete(imageId);
|
|
518
|
+
/** Source dimensions used by the frame writer to clip a direct placement. */
|
|
519
|
+
getPlacementGeometry(imageId: number): ImageDimensions | undefined {
|
|
520
|
+
return this.#placementGeometry.get(imageId);
|
|
598
521
|
}
|
|
599
522
|
|
|
600
523
|
/**
|
|
@@ -735,6 +658,8 @@ export class Image implements Component {
|
|
|
735
658
|
// (its rows may already be committed to native scrollback).
|
|
736
659
|
#renderedGraphicRows = 0;
|
|
737
660
|
#native?: NativeNode;
|
|
661
|
+
/** Newest SIXEL encode: its target size and, once settled, the sequence (`null` on failure). */
|
|
662
|
+
#sixel?: { widthPx: number; heightPx: number; sequence?: string | null };
|
|
738
663
|
|
|
739
664
|
constructor(
|
|
740
665
|
base64Data: string,
|
|
@@ -768,6 +693,43 @@ export class Image implements Component {
|
|
|
768
693
|
this.#cachedWidth = undefined;
|
|
769
694
|
}
|
|
770
695
|
|
|
696
|
+
releaseRenderCaches(): void {
|
|
697
|
+
this.#cachedLines = undefined;
|
|
698
|
+
this.#cachedWidth = undefined;
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
/**
|
|
702
|
+
* SIXEL sequence for a target size. A new size starts the encode off the JS
|
|
703
|
+
* thread and answers `undefined` until it settles; the settled encode
|
|
704
|
+
* invalidates the cached lines and requests a repaint. A render bound for
|
|
705
|
+
* scrollback cannot wait, so it encodes synchronously.
|
|
706
|
+
*/
|
|
707
|
+
#sixelSequence(widthPx: number, heightPx: number): string | null | undefined {
|
|
708
|
+
const current = this.#sixel;
|
|
709
|
+
const sameSize = current !== undefined && current.widthPx === widthPx && current.heightPx === heightPx;
|
|
710
|
+
if (sameSize && current.sequence !== undefined) return current.sequence;
|
|
711
|
+
if (scrollbackRenderDepth > 0) {
|
|
712
|
+
// Replacing the entry makes a still-pending async encode settle as stale.
|
|
713
|
+
const sequence = encodeSixelNow(this.#base64Data, widthPx, heightPx);
|
|
714
|
+
this.#sixel = { widthPx, heightPx, sequence };
|
|
715
|
+
return sequence;
|
|
716
|
+
}
|
|
717
|
+
if (sameSize) return undefined;
|
|
718
|
+
const request: { widthPx: number; heightPx: number; sequence?: string | null } = { widthPx, heightPx };
|
|
719
|
+
this.#sixel = request;
|
|
720
|
+
const settle = (sequence: string | null): void => {
|
|
721
|
+
request.sequence = sequence;
|
|
722
|
+
if (this.#sixel !== request) return;
|
|
723
|
+
this.invalidate();
|
|
724
|
+
if (this.#options.requestRender) this.#options.requestRender();
|
|
725
|
+
else this.#budget?.requestRender();
|
|
726
|
+
};
|
|
727
|
+
encodeSixelAsync(new Uint8Array(Buffer.from(this.#base64Data, "base64")), widthPx, heightPx).then(settle, () =>
|
|
728
|
+
settle(null),
|
|
729
|
+
);
|
|
730
|
+
return undefined;
|
|
731
|
+
}
|
|
732
|
+
|
|
771
733
|
/**
|
|
772
734
|
* A native `image` backed by a content-addressed blob; the terminal fits
|
|
773
735
|
* it. Cell caps become `ch`/`lines` bounds. The inline-image budget and
|
|
@@ -775,11 +737,9 @@ export class Image implements Component {
|
|
|
775
737
|
*/
|
|
776
738
|
describe(_cx: DescribeContext): NativeNode {
|
|
777
739
|
if (this.#native) return this.#native;
|
|
778
|
-
const blob = registerNativeBlob(Buffer.from(this.#base64Data, "base64"), this.#mimeType);
|
|
779
740
|
const maxW = this.#options.maxWidthCells;
|
|
780
741
|
const maxH = this.#options.maxHeightCells;
|
|
781
|
-
this.#native =
|
|
782
|
-
blob,
|
|
742
|
+
this.#native = nativeImageNode(Buffer.from(this.#base64Data, "base64"), this.#mimeType, {
|
|
783
743
|
alt: imageFallback(this.#mimeType, this.#dimensions, this.#options.filename),
|
|
784
744
|
w: this.#dimensions.widthPx,
|
|
785
745
|
h: this.#dimensions.heightPx,
|
|
@@ -820,13 +780,15 @@ export class Image implements Component {
|
|
|
820
780
|
this.#cachedCellWidthPx === cellDimensions.widthPx &&
|
|
821
781
|
this.#cachedCellHeightPx === cellDimensions.heightPx &&
|
|
822
782
|
this.#cachedKittyUnicodePlaceholders === kittyUnicodePlaceholders &&
|
|
823
|
-
(!transmitsSeparately || this.#budget?.shouldTransmit(imageId) !== true)
|
|
783
|
+
(!transmitsSeparately || this.#budget?.shouldTransmit(imageId) !== true) &&
|
|
784
|
+
// Cached rows reserved for a pending SIXEL encode must not reach scrollback.
|
|
785
|
+
!(scrollbackRenderDepth > 0 && this.#sixel !== undefined && this.#sixel.sequence === undefined)
|
|
824
786
|
) {
|
|
825
787
|
return this.#cachedLines;
|
|
826
788
|
}
|
|
827
789
|
|
|
828
790
|
const cap = this.#options.maxWidthCells;
|
|
829
|
-
const maxWidth = cap != null && cap > 0 ? Math.min(width
|
|
791
|
+
const maxWidth = cap != null && cap > 0 ? Math.min(imageMaxColumns(width), cap) : imageMaxColumns(width);
|
|
830
792
|
|
|
831
793
|
let lines: string[];
|
|
832
794
|
|
|
@@ -839,6 +801,7 @@ export class Image implements Component {
|
|
|
839
801
|
maxHeightCells: this.#options.maxHeightCells,
|
|
840
802
|
imageId: this.#imageId,
|
|
841
803
|
includeTransmit: needsTransmit,
|
|
804
|
+
sixel: (widthPx, heightPx) => this.#sixelSequence(widthPx, heightPx),
|
|
842
805
|
});
|
|
843
806
|
|
|
844
807
|
if (result?.transmit && this.#imageId != null && this.#budget !== undefined) {
|
|
@@ -28,6 +28,7 @@ export declare class Input implements Component, Focusable {
|
|
|
28
28
|
setValue(value: string): void;
|
|
29
29
|
setUseTerminalCursor(useTerminalCursor: boolean): void;
|
|
30
30
|
getUseTerminalCursor(): boolean;
|
|
31
|
+
capturesInput(data: string): boolean;
|
|
31
32
|
/**
|
|
32
33
|
* Apply one key: the editor's text bindings (motion, deletion, kill ring,
|
|
33
34
|
* undo), pastes and printable text. Returns whether the key was the
|
|
@@ -39,7 +40,7 @@ export declare class Input implements Component, Focusable {
|
|
|
39
40
|
/** Apply terminal paste semantics to text from non-bracketed paste transports
|
|
40
41
|
* (e.g. kitty's OSC 5522 enhanced clipboard read). Mirrors `Editor.pasteText`. */
|
|
41
42
|
pasteText(text: string): void;
|
|
42
|
-
/** Terminal-side selection edits on the `input` node (see {@link applyHostEdit}). */
|
|
43
|
+
/** Terminal-side selection edits and undo on the `input` node (see {@link applyHostEdit}). */
|
|
43
44
|
handleNativeEvent(event: NativeUiEvent): void;
|
|
44
45
|
/**
|
|
45
46
|
* Apply an edit the terminal made over its own selection (TSP `edit`):
|
package/src/components/input.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BracketedPasteHandler, decodeReencodedPasteControls } from "../bracketed-paste";
|
|
2
|
-
import { getKeybindings } from "../keybindings";
|
|
3
|
-
import { extractPrintableText,
|
|
2
|
+
import { canonicalKeyId, getKeybindings } from "../keybindings";
|
|
3
|
+
import { extractPrintableText, parseKey } from "../keys";
|
|
4
4
|
import { KillRing } from "../kill-ring";
|
|
5
5
|
import type { TspInputProps } from "@linxiraos/pi-wire";
|
|
6
6
|
import { node } from "../native/describe";
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
type NativeUiEvent,
|
|
15
15
|
resolveTextEdit,
|
|
16
16
|
} from "../native/node";
|
|
17
|
-
import { SpaceHoldGesture } from "../space-hold";
|
|
17
|
+
import { getSpaceHoldText, SpaceHoldGesture } from "../space-hold";
|
|
18
18
|
import { type Component, CURSOR_MARKER, type Focusable } from "../tui";
|
|
19
19
|
import { cursorColumnWindow } from "./scroll-viewport";
|
|
20
20
|
import {
|
|
@@ -134,6 +134,10 @@ export class Input implements Component, Focusable {
|
|
|
134
134
|
return this.#useTerminalCursor;
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
+
capturesInput(data: string): boolean {
|
|
138
|
+
return this.spaceHold.shouldRoute(data);
|
|
139
|
+
}
|
|
140
|
+
|
|
137
141
|
/**
|
|
138
142
|
* Apply one key: the editor's text bindings (motion, deletion, kill ring,
|
|
139
143
|
* undo), pastes and printable text. Returns whether the key was the
|
|
@@ -154,10 +158,14 @@ export class Input implements Component, Focusable {
|
|
|
154
158
|
return true;
|
|
155
159
|
}
|
|
156
160
|
|
|
157
|
-
//
|
|
158
|
-
|
|
161
|
+
// Reserve configured keys before text bindings or submit/delete handlers can consume them.
|
|
162
|
+
const parsedKey = parseKey(data);
|
|
163
|
+
const canonical = parsedKey !== undefined ? canonicalKeyId(parsedKey) : undefined;
|
|
164
|
+
const spaceHoldText = getSpaceHoldText(data, canonical);
|
|
165
|
+
switch (this.spaceHold.process(canonical, spaceHoldText?.length ?? 0)) {
|
|
159
166
|
case "type":
|
|
160
|
-
this.#insertCharacter(
|
|
167
|
+
this.#insertCharacter(spaceHoldText!);
|
|
168
|
+
this.spaceHold.recordTyped(spaceHoldText!.length);
|
|
161
169
|
return true;
|
|
162
170
|
case "swallow":
|
|
163
171
|
return true;
|
|
@@ -284,9 +292,10 @@ export class Input implements Component, Focusable {
|
|
|
284
292
|
this.#handlePaste(text);
|
|
285
293
|
}
|
|
286
294
|
|
|
287
|
-
/** Terminal-side selection edits on the `input` node (see {@link applyHostEdit}). */
|
|
295
|
+
/** Terminal-side selection edits and undo on the `input` node (see {@link applyHostEdit}). */
|
|
288
296
|
handleNativeEvent(event: NativeUiEvent): void {
|
|
289
297
|
if (event.type === "edit") this.applyHostEdit(event);
|
|
298
|
+
else if (event.type === "undo") this.#undo();
|
|
290
299
|
}
|
|
291
300
|
|
|
292
301
|
/**
|
|
@@ -582,6 +591,7 @@ export class Input implements Component, Focusable {
|
|
|
582
591
|
const props: TspInputProps = {
|
|
583
592
|
text: value,
|
|
584
593
|
cursor,
|
|
594
|
+
sendable: false,
|
|
585
595
|
prompt: prompt || undefined,
|
|
586
596
|
placeholder: this.placeholder,
|
|
587
597
|
};
|
|
@@ -21,6 +21,7 @@ export declare class KeyValueList implements Component {
|
|
|
21
21
|
constructor(rows: readonly KeyValueRow[], options: KeyValueListOptions);
|
|
22
22
|
setRows(rows: readonly KeyValueRow[]): boolean;
|
|
23
23
|
invalidate(): void;
|
|
24
|
+
releaseRenderCaches(): void;
|
|
24
25
|
/** An aligned `kv` grid; label width and value wrapping are the terminal's. */
|
|
25
26
|
describe(_cx: DescribeContext): NativeNode;
|
|
26
27
|
render(width: number): readonly string[];
|
|
@@ -54,6 +54,10 @@ export class KeyValueList implements Component {
|
|
|
54
54
|
this.#native = undefined;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
releaseRenderCaches(): void {
|
|
58
|
+
this.#cache = undefined;
|
|
59
|
+
}
|
|
60
|
+
|
|
57
61
|
/** An aligned `kv` grid; label width and value wrapping are the terminal's. */
|
|
58
62
|
describe(_cx: DescribeContext): NativeNode {
|
|
59
63
|
this.#native ??= node("kv", {
|
|
@@ -48,6 +48,7 @@ export declare class Row implements Component, MouseRoutable {
|
|
|
48
48
|
setDecorations(prefix: LayoutDecoration | undefined, gap: LayoutDecoration | undefined, suffix: LayoutDecoration | undefined): void;
|
|
49
49
|
setIgnoreTight(ignore: boolean): this;
|
|
50
50
|
invalidate(): void;
|
|
51
|
+
releaseRenderCaches(): void;
|
|
51
52
|
dispose(): void;
|
|
52
53
|
/**
|
|
53
54
|
* A flex `row`: each child sits in a slot carrying its width bounds
|
|
@@ -168,6 +168,13 @@ export class Row implements Component, MouseRoutable {
|
|
|
168
168
|
}
|
|
169
169
|
}
|
|
170
170
|
|
|
171
|
+
releaseRenderCaches(): void {
|
|
172
|
+
this.#memo = undefined;
|
|
173
|
+
for (const child of uniqueLayoutComponents(this.#children.map(item => item.content))) {
|
|
174
|
+
child.releaseRenderCaches?.();
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
171
178
|
dispose(): void {
|
|
172
179
|
for (const child of uniqueLayoutComponents(this.#children.map(item => item.content))) {
|
|
173
180
|
child.dispose?.();
|
|
@@ -43,6 +43,7 @@ export declare class Stack implements Component, MouseRoutable {
|
|
|
43
43
|
setHeight(height: number | undefined): void;
|
|
44
44
|
setIgnoreTight(ignore: boolean): this;
|
|
45
45
|
invalidate(): void;
|
|
46
|
+
releaseRenderCaches(): void;
|
|
46
47
|
dispose(): void;
|
|
47
48
|
/**
|
|
48
49
|
* A `col` of slots carrying each child's height bounds (fixed → min = max
|
|
@@ -140,6 +140,13 @@ export class Stack implements Component, MouseRoutable {
|
|
|
140
140
|
}
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
+
releaseRenderCaches(): void {
|
|
144
|
+
this.#memo = undefined;
|
|
145
|
+
for (const child of uniqueLayoutComponents(this.#children.map(item => item.content))) {
|
|
146
|
+
child.releaseRenderCaches?.();
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
143
150
|
dispose(): void {
|
|
144
151
|
for (const child of uniqueLayoutComponents(this.#children.map(item => item.content))) {
|
|
145
152
|
child.dispose?.();
|