@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
|
@@ -10,17 +10,16 @@ import {
|
|
|
10
10
|
} from "../components/editor";
|
|
11
11
|
import { addKeyAliases, canonicalKeyId, getKeybindings } from "../keybindings";
|
|
12
12
|
import { type KeyId, parseKey, parseKittySequence } from "../keys";
|
|
13
|
-
import { SpaceHoldGesture } from "../space-hold";
|
|
13
|
+
import { getSpaceHoldText, SpaceHoldGesture } from "../space-hold";
|
|
14
14
|
import { type Component, TUI } from "../tui";
|
|
15
15
|
import type { AppKeybinding } from "../app-keybindings";
|
|
16
|
-
import {
|
|
16
|
+
import { formatTooltipKey } from "../key-hint-format";
|
|
17
17
|
import { MAIN_AGENT_ID } from "../overlays/agent-hub-types";
|
|
18
18
|
import { compact, keyed, node, row, span } from "../native/describe";
|
|
19
19
|
import type { DescribeContext, NativeNode, NativeUiEvent } from "../native/node";
|
|
20
20
|
import type { ComposerFacts, ComposerFactsSource } from "../status-line/types";
|
|
21
21
|
import { allowsModelMentions, allowsSkillTokens, SKILL_TOKEN_RE } from "./skill-tokens";
|
|
22
22
|
import { expandModelMentionTags, MODEL_MENTION_RE, modelMentionToken } from "./model-mention-syntax";
|
|
23
|
-
import { imageAttachmentSource } from "./image-source";
|
|
24
23
|
import { isVideoPath } from "./video";
|
|
25
24
|
import {
|
|
26
25
|
attachmentSgr,
|
|
@@ -41,7 +40,7 @@ import {
|
|
|
41
40
|
} from "./composer-attachments";
|
|
42
41
|
import { type MacOSSpellingFeatures, MacOSSpellingProvider } from "./macos-spelling";
|
|
43
42
|
import { hasMagicKeyword, highlightMagicKeywords, magicKeywordRanges } from "./magic-keywords";
|
|
44
|
-
import type { TspEditorDecoration } from "@linxiraos/pi-wire";
|
|
43
|
+
import type { TspEditorDecoration, TspText } from "@linxiraos/pi-wire";
|
|
45
44
|
import { isNativeRendering } from "../native/state";
|
|
46
45
|
import { isQueuedMessageList, parseQueueShorthand, QUEUE_LIST_MARKER_RE } from "./queue-input";
|
|
47
46
|
import { type WordCompletionMethod, WordCompletionProvider } from "./word-completion";
|
|
@@ -50,8 +49,8 @@ import { fgOrPlain, theme } from "../theme/theme";
|
|
|
50
49
|
/** A shell-mode draft's sigil (`!`, `!!`, `$`, `$$`) with its surrounding blanks; the mode chip stands in for it natively. */
|
|
51
50
|
const SHELL_SIGIL_RE = /^\s*(?:!!?|\$\$?)[ \t]?/;
|
|
52
51
|
|
|
53
|
-
/** The
|
|
54
|
-
const NATIVE_COMPOSER_PLACEHOLDER = "
|
|
52
|
+
/** The untitled session's TSP placeholder; the rotating ANSI hints (thinking effort, …) move into control tooltips. */
|
|
53
|
+
const NATIVE_COMPOSER_PLACEHOLDER = "What are we cooking?";
|
|
55
54
|
|
|
56
55
|
/** Live composer state the TSP layout shows; the interactive host wires {@link CustomEditor.composerState}. */
|
|
57
56
|
export interface ComposerNativeState {
|
|
@@ -59,10 +58,16 @@ export interface ComposerNativeState {
|
|
|
59
58
|
readonly shell?: { readonly kind: "bash" | "python"; readonly excluded: boolean };
|
|
60
59
|
/** Thinking effort word (`high`, `off`, `auto`); undefined when the model has no thinking. */
|
|
61
60
|
readonly thinking?: string;
|
|
61
|
+
/** The thinking level draws as the model chip's icon instead of its own chip (where the terminal has `effort`). */
|
|
62
|
+
readonly thinkingInModel?: boolean;
|
|
63
|
+
/** Generation tok/s after the thinking level (live while running, else the last reading); undefined hides it. */
|
|
64
|
+
readonly rate?: number;
|
|
62
65
|
/** A turn is running: the send keycap becomes a Stop button. */
|
|
63
66
|
readonly running: boolean;
|
|
64
67
|
/** The viewed subagent's lineage, outermost first and the viewed agent last; undefined on the main session. */
|
|
65
68
|
readonly viewing?: readonly string[];
|
|
69
|
+
/** The session's title, quoted in italics as the TSP placeholder; undefined until it has one. */
|
|
70
|
+
readonly title?: string;
|
|
66
71
|
}
|
|
67
72
|
|
|
68
73
|
/** Action code prefix of the viewing header's links: `focus:<agent id>`, {@link MAIN_AGENT_ID} for the main session. */
|
|
@@ -70,6 +75,15 @@ const FOCUS_ACTION = "focus:";
|
|
|
70
75
|
|
|
71
76
|
const IDLE_COMPOSER: ComposerNativeState = { running: false };
|
|
72
77
|
|
|
78
|
+
/** The composer's controls around the input, rebuilt when what they show changes; the bar adds the rate and facts. */
|
|
79
|
+
interface ComposerControls {
|
|
80
|
+
readonly focus: NativeNode | undefined;
|
|
81
|
+
readonly mode: NativeNode | undefined;
|
|
82
|
+
readonly model: NativeNode | undefined;
|
|
83
|
+
readonly effort: NativeNode | undefined;
|
|
84
|
+
readonly submit: NativeNode;
|
|
85
|
+
}
|
|
86
|
+
|
|
73
87
|
/**
|
|
74
88
|
* Filled steps (of four) of the effort chip's fallback meter, for terminals without the `effort`
|
|
75
89
|
* kind; `auto` before it resolves has none known.
|
|
@@ -197,11 +211,17 @@ function normalizePastedPath(path: string): string {
|
|
|
197
211
|
try {
|
|
198
212
|
return url.fileURLToPath(unquoted);
|
|
199
213
|
} catch {
|
|
200
|
-
//
|
|
201
|
-
//
|
|
214
|
+
// Windows rejects drive-less URLs (`file:///Users/…`, forwarded from a
|
|
215
|
+
// macOS pasteboard or a remote session); decode them as POSIX paths.
|
|
216
|
+
try {
|
|
217
|
+
return url.fileURLToPath(unquoted, { windows: false });
|
|
218
|
+
} catch {
|
|
219
|
+
// Malformed file URL: drop through to the shell-unescape branch
|
|
220
|
+
// so the caller can still reject it as a non-explicit path.
|
|
221
|
+
}
|
|
202
222
|
}
|
|
203
223
|
}
|
|
204
|
-
return unquoted.replace(SHELL_ESCAPED_PATH_CHAR_REGEX, "$1");
|
|
224
|
+
return (unquoted.startsWith("\\~/") ? unquoted.slice(1) : unquoted).replace(SHELL_ESCAPED_PATH_CHAR_REGEX, "$1");
|
|
205
225
|
}
|
|
206
226
|
|
|
207
227
|
function isExplicitPastedPath(path: string): boolean {
|
|
@@ -389,7 +409,7 @@ export function extractImagePathFromText(text: string): string | undefined {
|
|
|
389
409
|
|
|
390
410
|
/**
|
|
391
411
|
* Resolve the {@link EditorTheme} from a `CustomEditor`/`Editor` constructor
|
|
392
|
-
* argument list, tolerating both the
|
|
412
|
+
* argument list, tolerating both the omp `(theme)` and upstream-pi
|
|
393
413
|
* `(tui, theme, keybindings)` conventions (see {@link CustomEditor}'s
|
|
394
414
|
* constructor). A real `EditorTheme` is identified structurally — it exposes a
|
|
395
415
|
* `borderColor` function and a `symbols` object — so a `TUI` passed in the first
|
|
@@ -459,13 +479,13 @@ export class CustomEditor extends Editor {
|
|
|
459
479
|
chips: ComposerChipDescriptor[];
|
|
460
480
|
}
|
|
461
481
|
| undefined;
|
|
462
|
-
/** Host-wired producer of per-image
|
|
463
|
-
* chip tokens for restored drafts (esc-esc, `/tree`, branch). */
|
|
482
|
+
/** Host-wired producer of per-image chip targets (a file on disk, or a session blob copy);
|
|
483
|
+
* drives clickable chip tokens for restored drafts (esc-esc, `/tree`, branch). */
|
|
464
484
|
draftImageLinkMaterializer?: (images: readonly ImageContent[]) => Promise<(string | undefined)[] | undefined>;
|
|
465
485
|
|
|
466
486
|
/**
|
|
467
487
|
* The host {@link TUI}, captured when a plugin constructs this editor through
|
|
468
|
-
* the upstream-pi `(tui, theme, keybindings)` convention. Undefined for
|
|
488
|
+
* the upstream-pi `(tui, theme, keybindings)` convention. Undefined for omp's
|
|
469
489
|
* own `new CustomEditor(theme)` callers (they drive repaints through the
|
|
470
490
|
* interactive-mode wiring instead). Plugins that call `this.tui.requestRender()`
|
|
471
491
|
* in their overrides read it here (issue #4766).
|
|
@@ -473,7 +493,7 @@ export class CustomEditor extends Editor {
|
|
|
473
493
|
tui?: TUI;
|
|
474
494
|
|
|
475
495
|
/**
|
|
476
|
-
* Accept both the
|
|
496
|
+
* Accept both the omp constructor convention — `new CustomEditor(theme)` —
|
|
477
497
|
* and the upstream-pi `Editor` convention — `new Editor(tui, theme, keybindings)`
|
|
478
498
|
* — that {@link ExtensionUIContext.setEditorComponent}'s factory contract
|
|
479
499
|
* advertises `(tui, theme, keybindings)`. Plugins written against upstream pi
|
|
@@ -770,7 +790,7 @@ export class CustomEditor extends Editor {
|
|
|
770
790
|
if (!materialize || images.length === 0) return;
|
|
771
791
|
const links = await materialize(images);
|
|
772
792
|
if (!links || this.pendingImages !== images) return;
|
|
773
|
-
this.pendingImageLinks =
|
|
793
|
+
this.pendingImageLinks = links;
|
|
774
794
|
this.imageLinks = this.pendingImageLinks;
|
|
775
795
|
this.#requestShimmerRepaint?.();
|
|
776
796
|
}
|
|
@@ -779,10 +799,21 @@ export class CustomEditor extends Editor {
|
|
|
779
799
|
* indivisible: a stray backspace deletes the whole token instead of corrupting it. */
|
|
780
800
|
override atomicTokenPattern = COMPOSER_TOKEN_REGEX;
|
|
781
801
|
|
|
802
|
+
/** Atom-table revision and pattern the composer token matcher was last built for. */
|
|
803
|
+
#tokenPatternAtomsRevision = -1;
|
|
804
|
+
#tokenPattern: RegExp | undefined;
|
|
805
|
+
|
|
782
806
|
#syncComposerTokenPattern(): void {
|
|
783
|
-
|
|
807
|
+
// Rebuild only when the atom table changed or someone replaced the pattern since the last sync.
|
|
808
|
+
if (this.#tokenPatternAtomsRevision === this.atomsRevision && this.#tokenPattern === this.atomicTokenPattern) {
|
|
809
|
+
return;
|
|
810
|
+
}
|
|
811
|
+
const labels: string[] = [];
|
|
812
|
+
for (const [label, expansion] of this.atoms) if (expansion.startsWith("^")) labels.push(label);
|
|
784
813
|
const next = composerTokenRegex(labels);
|
|
785
814
|
if (next.source !== this.atomicTokenPattern.source) this.atomicTokenPattern = next;
|
|
815
|
+
this.#tokenPatternAtomsRevision = this.atomsRevision;
|
|
816
|
+
this.#tokenPattern = this.atomicTokenPattern;
|
|
786
817
|
}
|
|
787
818
|
|
|
788
819
|
/** Magic-keyword shimmer cadence — drives one editor repaint every 70 ms while
|
|
@@ -800,27 +831,35 @@ export class CustomEditor extends Editor {
|
|
|
800
831
|
* timer to request the next animation frame. Undefined when nobody is
|
|
801
832
|
* listening (tests, headless callers); the timer chain still self-cleans. */
|
|
802
833
|
#requestShimmerRepaint: (() => void) | undefined;
|
|
803
|
-
|
|
834
|
+
/** Text revision the per-revision decoration inputs below were computed for. */
|
|
835
|
+
#decorationRevision = -1;
|
|
836
|
+
#decorationText = "";
|
|
804
837
|
#decorationLines: readonly string[] = [""];
|
|
805
838
|
#queueShorthandActive = false;
|
|
806
839
|
#queueListActive = false;
|
|
807
840
|
|
|
808
841
|
/** Decorate magic keywords, attachments, and the queue-composer header/list markers.
|
|
809
842
|
* Queue shorthand reserves its first logical line as a dim `Queueing` label; sequential
|
|
810
|
-
* item markers use the accent color so separate follow-ups remain visible while composing.
|
|
843
|
+
* item markers use the accent color so separate follow-ups remain visible while composing.
|
|
844
|
+
* Called once per layout segment, so whole-buffer inputs are computed once per text revision. */
|
|
811
845
|
override decorateText = (text: string, context: EditorTextDecorationContext): string => {
|
|
812
846
|
this.#syncComposerTokenPattern();
|
|
813
|
-
|
|
847
|
+
if (this.#decorationRevision !== this.textRevision) {
|
|
848
|
+
this.#decorationRevision = this.textRevision;
|
|
849
|
+
const editorText = this.getText();
|
|
850
|
+
if (this.#decorationText !== editorText) {
|
|
851
|
+
this.#decorationText = editorText;
|
|
852
|
+
this.#decorationLines = this.getLines();
|
|
853
|
+
const queueBody = parseQueueShorthand(editorText);
|
|
854
|
+
this.#queueShorthandActive = queueBody !== undefined;
|
|
855
|
+
this.#queueListActive = queueBody !== undefined && isQueuedMessageList(queueBody);
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
// One string instance per revision: whole-buffer memos downstream hit on identity.
|
|
859
|
+
const editorText = this.#decorationText;
|
|
814
860
|
const animated = this.focused && this.#shimmerEnabled() && hasMagicKeyword(editorText);
|
|
815
861
|
const phase = animated ? (Date.now() % CustomEditor.SHIMMER_PERIOD_MS) / CustomEditor.SHIMMER_PERIOD_MS : 0;
|
|
816
862
|
if (animated) this.#scheduleShimmerFrame();
|
|
817
|
-
if (this.#queueDecorationText !== editorText) {
|
|
818
|
-
this.#queueDecorationText = editorText;
|
|
819
|
-
this.#decorationLines = this.getLines();
|
|
820
|
-
const queueBody = parseQueueShorthand(editorText);
|
|
821
|
-
this.#queueShorthandActive = queueBody !== undefined;
|
|
822
|
-
this.#queueListActive = queueBody !== undefined && isQueuedMessageList(queueBody);
|
|
823
|
-
}
|
|
824
863
|
let sourceSearchOffset = 0;
|
|
825
864
|
const locateSource = (value: string): number => {
|
|
826
865
|
const offset = text.indexOf(value, sourceSearchOffset);
|
|
@@ -995,10 +1034,22 @@ export class CustomEditor extends Editor {
|
|
|
995
1034
|
}, CustomEditor.SHIMMER_FRAME_MS);
|
|
996
1035
|
this.#shimmerTimer.unref?.();
|
|
997
1036
|
}
|
|
998
|
-
/**
|
|
999
|
-
override
|
|
1000
|
-
|
|
1001
|
-
|
|
1037
|
+
/** Editing is available during bootstrap; atomic sends wait until submission is wired and enabled. */
|
|
1038
|
+
protected override get nativeSendable(): boolean {
|
|
1039
|
+
return this.onSubmit !== undefined && !this.disableSubmit;
|
|
1040
|
+
}
|
|
1041
|
+
/** The quoted-title placeholder, kept while the title stands so the editor node stays reused. */
|
|
1042
|
+
#titlePlaceholder: { title: string; text: TspText } | undefined;
|
|
1043
|
+
/** Viewing a subagent, the draft goes to it: the placeholder names it; else the session's title, quoted in italics. */
|
|
1044
|
+
override describePlaceholder = (): TspText => {
|
|
1045
|
+
const { viewing, title } = this.composerState();
|
|
1046
|
+
const agent = viewing?.at(-1);
|
|
1047
|
+
if (agent !== undefined) return `Message ${agent}`;
|
|
1048
|
+
if (!title) return NATIVE_COMPOSER_PLACEHOLDER;
|
|
1049
|
+
if (this.#titlePlaceholder?.title !== title) {
|
|
1050
|
+
this.#titlePlaceholder = { title, text: [{ t: `“${title}”`, s: "em" }] };
|
|
1051
|
+
}
|
|
1052
|
+
return this.#titlePlaceholder.text;
|
|
1002
1053
|
};
|
|
1003
1054
|
/** A shell-mode draft highlights as its language. */
|
|
1004
1055
|
override describeLanguage = (): string | undefined => this.composerState().shell?.kind;
|
|
@@ -1014,8 +1065,8 @@ export class CustomEditor extends Editor {
|
|
|
1014
1065
|
facts: ComposerFacts | undefined;
|
|
1015
1066
|
chips: Component | undefined;
|
|
1016
1067
|
input: NativeNode;
|
|
1017
|
-
|
|
1018
|
-
|
|
1068
|
+
rate: number | undefined;
|
|
1069
|
+
controls: ComposerControls;
|
|
1019
1070
|
bar: NativeNode;
|
|
1020
1071
|
layout: NativeEditorLayout;
|
|
1021
1072
|
}
|
|
@@ -1025,7 +1076,8 @@ export class CustomEditor extends Editor {
|
|
|
1025
1076
|
* The TSP composer: role `zeta.editor[.bash|.python]` (tone `pending` while
|
|
1026
1077
|
* a turn runs) over the context hairline, the viewing header while a
|
|
1027
1078
|
* subagent is focused, the attachment chips, a `line` row of the
|
|
1028
|
-
* shell-mode chip and the input, and the `bar`: model chip, effort chip
|
|
1079
|
+
* shell-mode chip and the input, and the `bar`: model chip, effort chip
|
|
1080
|
+
* (or the effort glyph as the model chip's icon), the generation rate,
|
|
1029
1081
|
* the other status facts, usage, then send (Stop while a turn runs).
|
|
1030
1082
|
* Clicks come back as `status.model`, `thinking.cycle`, `submit`,
|
|
1031
1083
|
* `interrupt` and `focus:<id>` actions.
|
|
@@ -1048,27 +1100,61 @@ export class CustomEditor extends Editor {
|
|
|
1048
1100
|
interruptKey,
|
|
1049
1101
|
state.viewing?.join("\u0001"),
|
|
1050
1102
|
effortGlyph,
|
|
1103
|
+
state.thinkingInModel,
|
|
1051
1104
|
].join("\0");
|
|
1105
|
+
const rate = state.rate;
|
|
1052
1106
|
const chips = this.attachmentChips;
|
|
1053
1107
|
const memo = this.#nativeComposer;
|
|
1054
|
-
if (
|
|
1108
|
+
if (
|
|
1109
|
+
memo &&
|
|
1110
|
+
memo.key === key &&
|
|
1111
|
+
memo.rate === rate &&
|
|
1112
|
+
memo.facts === facts &&
|
|
1113
|
+
memo.input === input &&
|
|
1114
|
+
memo.chips === chips
|
|
1115
|
+
) {
|
|
1055
1116
|
return memo.layout;
|
|
1056
1117
|
}
|
|
1057
|
-
const
|
|
1118
|
+
const controls =
|
|
1058
1119
|
memo?.key === key && memo.facts === facts
|
|
1059
|
-
? memo
|
|
1120
|
+
? memo.controls
|
|
1060
1121
|
: this.#describeComposerControls(state, facts, thinkingKey, modelKey, interruptKey, effortGlyph);
|
|
1122
|
+
// The rate ticks while a turn streams: only the bar follows it.
|
|
1123
|
+
const bar =
|
|
1124
|
+
memo?.controls === controls && memo.rate === rate
|
|
1125
|
+
? memo.bar
|
|
1126
|
+
: keyed(
|
|
1127
|
+
row(
|
|
1128
|
+
compact([
|
|
1129
|
+
controls.model,
|
|
1130
|
+
controls.effort,
|
|
1131
|
+
rate !== undefined &&
|
|
1132
|
+
node(
|
|
1133
|
+
"rate",
|
|
1134
|
+
{ value: rate, unit: "tok/s", role: "zeta.composer.rate", title: "Generation rate" },
|
|
1135
|
+
undefined,
|
|
1136
|
+
"rate",
|
|
1137
|
+
),
|
|
1138
|
+
// The status facts are the bar's flexible space; without them a spacer keeps send at the end.
|
|
1139
|
+
facts?.extras ?? node("row", { grow: 1 }, [], "gap"),
|
|
1140
|
+
facts?.usage,
|
|
1141
|
+
controls.submit,
|
|
1142
|
+
]),
|
|
1143
|
+
{ role: "zeta.composer.bar", gap: "sm", align: "center" },
|
|
1144
|
+
),
|
|
1145
|
+
"bar",
|
|
1146
|
+
);
|
|
1061
1147
|
const line = keyed(
|
|
1062
|
-
row(compact([mode, input]), { role: "zeta.composer.line", align: "start", gap: "sm" }),
|
|
1148
|
+
row(compact([controls.mode, input]), { role: "zeta.composer.line", align: "start", gap: "sm" }),
|
|
1063
1149
|
"line",
|
|
1064
1150
|
);
|
|
1065
1151
|
const layout: NativeEditorLayout = {
|
|
1066
1152
|
role: shell ? `zeta.editor.${shell.kind}` : "zeta.editor",
|
|
1067
1153
|
tone: state.running ? "pending" : undefined,
|
|
1068
|
-
children: compact([facts?.context, focus, chips, line, bar]),
|
|
1154
|
+
children: compact([facts?.context, controls.focus, chips, line, bar]),
|
|
1069
1155
|
caret: "line/input",
|
|
1070
1156
|
};
|
|
1071
|
-
this.#nativeComposer = { key, facts, chips, input,
|
|
1157
|
+
this.#nativeComposer = { key, facts, chips, input, rate, controls, bar, layout };
|
|
1072
1158
|
return layout;
|
|
1073
1159
|
};
|
|
1074
1160
|
|
|
@@ -1081,7 +1167,7 @@ export class CustomEditor extends Editor {
|
|
|
1081
1167
|
#describeViewing(viewing: readonly string[], interruptKey: KeyId): NativeNode | undefined {
|
|
1082
1168
|
const agent = viewing.at(-1);
|
|
1083
1169
|
if (agent === undefined) return undefined;
|
|
1084
|
-
const back =
|
|
1170
|
+
const back = formatTooltipKey(interruptKey);
|
|
1085
1171
|
const crumbs = viewing.slice(0, -1).map(id =>
|
|
1086
1172
|
node(
|
|
1087
1173
|
"text",
|
|
@@ -1130,7 +1216,7 @@ export class CustomEditor extends Editor {
|
|
|
1130
1216
|
);
|
|
1131
1217
|
}
|
|
1132
1218
|
|
|
1133
|
-
/** The viewing header over the text, the shell-mode chip before the input, and the bar under it. */
|
|
1219
|
+
/** The viewing header over the text, the shell-mode chip before the input, and the bar's controls under it. */
|
|
1134
1220
|
#describeComposerControls(
|
|
1135
1221
|
state: ComposerNativeState,
|
|
1136
1222
|
facts: ComposerFacts | undefined,
|
|
@@ -1138,9 +1224,26 @@ export class CustomEditor extends Editor {
|
|
|
1138
1224
|
modelKey: KeyId | undefined,
|
|
1139
1225
|
interruptKey: KeyId,
|
|
1140
1226
|
effortGlyph: boolean,
|
|
1141
|
-
):
|
|
1227
|
+
): ComposerControls {
|
|
1142
1228
|
const shell = state.shell;
|
|
1143
1229
|
const focus = state.viewing && this.#describeViewing(state.viewing, interruptKey);
|
|
1230
|
+
const thinking = state.thinking;
|
|
1231
|
+
const thinkingHint = thinkingKey ? ` ${formatTooltipKey(thinkingKey)}` : "";
|
|
1232
|
+
// The level collapses into the model chip's icon; its own tooltip and click stay.
|
|
1233
|
+
const modelIcon =
|
|
1234
|
+
facts && thinking !== undefined && effortGlyph && state.thinkingInModel
|
|
1235
|
+
? node(
|
|
1236
|
+
"effort",
|
|
1237
|
+
{
|
|
1238
|
+
level: thinking,
|
|
1239
|
+
role: "zeta.composer.model.effort",
|
|
1240
|
+
title: `Thinking effort: ${thinking}${thinkingHint}`,
|
|
1241
|
+
actions: { click: "thinking.cycle" },
|
|
1242
|
+
},
|
|
1243
|
+
undefined,
|
|
1244
|
+
"effort",
|
|
1245
|
+
)
|
|
1246
|
+
: undefined;
|
|
1144
1247
|
const model =
|
|
1145
1248
|
facts &&
|
|
1146
1249
|
node(
|
|
@@ -1150,42 +1253,42 @@ export class CustomEditor extends Editor {
|
|
|
1150
1253
|
gap: "xs",
|
|
1151
1254
|
align: "center",
|
|
1152
1255
|
tone: facts.model.tone,
|
|
1153
|
-
title: modelKey ? `Switch model ${
|
|
1256
|
+
title: modelKey ? `Switch model ${formatTooltipKey(modelKey)}` : "Switch model",
|
|
1154
1257
|
actions: { click: "status.model" },
|
|
1155
1258
|
},
|
|
1156
1259
|
[
|
|
1157
|
-
node("icon", { name: "model" }, undefined, "icon"),
|
|
1260
|
+
modelIcon ?? node("icon", { name: "model" }, undefined, "icon"),
|
|
1158
1261
|
node("text", { spans: facts.model.spans, wrap: "none" }, undefined, "name"),
|
|
1159
1262
|
node("icon", { name: "chev" }, undefined, "chev"),
|
|
1160
1263
|
],
|
|
1161
1264
|
"model",
|
|
1162
1265
|
);
|
|
1163
|
-
const thinking = state.thinking;
|
|
1164
1266
|
const effortSteps = thinking === undefined ? undefined : EFFORT_STEPS[thinking];
|
|
1165
1267
|
const effort =
|
|
1166
|
-
thinking !== undefined &&
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1268
|
+
thinking !== undefined && !modelIcon
|
|
1269
|
+
? node(
|
|
1270
|
+
"row",
|
|
1271
|
+
{
|
|
1272
|
+
role: "zeta.composer.effort",
|
|
1273
|
+
gap: "xs",
|
|
1274
|
+
align: "center",
|
|
1275
|
+
title: `Thinking effort${thinkingHint}`,
|
|
1276
|
+
actions: { click: "thinking.cycle" },
|
|
1277
|
+
},
|
|
1278
|
+
[
|
|
1279
|
+
effortGlyph
|
|
1280
|
+
? node("effort", { level: thinking }, undefined, "glyph")
|
|
1281
|
+
: node(
|
|
1282
|
+
"meter",
|
|
1283
|
+
{ value: effortSteps === undefined ? null : effortSteps / 4, style: "blocks", steps: 4 },
|
|
1284
|
+
undefined,
|
|
1285
|
+
"meter",
|
|
1286
|
+
),
|
|
1287
|
+
node("text", { text: thinking, wrap: "none" }, undefined, "level"),
|
|
1288
|
+
],
|
|
1289
|
+
"effort",
|
|
1290
|
+
)
|
|
1291
|
+
: undefined;
|
|
1189
1292
|
const submit = state.running
|
|
1190
1293
|
? node(
|
|
1191
1294
|
"text",
|
|
@@ -1193,7 +1296,7 @@ export class CustomEditor extends Editor {
|
|
|
1193
1296
|
role: "zeta.composer.stop",
|
|
1194
1297
|
text: "Stop",
|
|
1195
1298
|
tone: "error",
|
|
1196
|
-
title: `Stop ${
|
|
1299
|
+
title: `Stop ${formatTooltipKey(interruptKey)}`,
|
|
1197
1300
|
actions: { click: "interrupt" },
|
|
1198
1301
|
},
|
|
1199
1302
|
undefined,
|
|
@@ -1204,22 +1307,13 @@ export class CustomEditor extends Editor {
|
|
|
1204
1307
|
{
|
|
1205
1308
|
role: "zeta.composer.send",
|
|
1206
1309
|
keys: ["enter"],
|
|
1207
|
-
title: `Send ${
|
|
1310
|
+
title: `Send ${formatTooltipKey("enter")}`,
|
|
1208
1311
|
actions: { click: "submit" },
|
|
1209
1312
|
},
|
|
1210
1313
|
undefined,
|
|
1211
1314
|
"send",
|
|
1212
1315
|
);
|
|
1213
|
-
|
|
1214
|
-
const bar = keyed(
|
|
1215
|
-
row(compact([model, effort, facts?.extras ?? node("row", { grow: 1 }, [], "gap"), facts?.usage, submit]), {
|
|
1216
|
-
role: "zeta.composer.bar",
|
|
1217
|
-
gap: "sm",
|
|
1218
|
-
align: "center",
|
|
1219
|
-
}),
|
|
1220
|
-
"bar",
|
|
1221
|
-
);
|
|
1222
|
-
if (!shell) return { focus, mode: undefined, bar };
|
|
1316
|
+
if (!shell) return { focus, mode: undefined, model, effort, submit };
|
|
1223
1317
|
const runs = shell.kind === "bash" ? "Runs in your shell" : "Runs in Python";
|
|
1224
1318
|
const mode = keyed(
|
|
1225
1319
|
row(
|
|
@@ -1242,16 +1336,30 @@ export class CustomEditor extends Editor {
|
|
|
1242
1336
|
),
|
|
1243
1337
|
"mode",
|
|
1244
1338
|
);
|
|
1245
|
-
return { focus, mode,
|
|
1339
|
+
return { focus, mode, model, effort, submit };
|
|
1246
1340
|
}
|
|
1247
1341
|
|
|
1248
1342
|
/**
|
|
1249
1343
|
* Clicks on the composer's controls take the same paths as their keys: ⇧⇥,
|
|
1250
1344
|
* ⏎ and Esc; the viewing header's links (`focus:<id>`) go to the host,
|
|
1251
1345
|
* the status facts' clicks (`status.*`) to their source. Selection edits
|
|
1252
|
-
* go to the buffer
|
|
1346
|
+
* go to the buffer; `send` submits its own prompt after saving the old draft
|
|
1347
|
+
* for recall and waiting for in-flight clipboard work.
|
|
1253
1348
|
*/
|
|
1254
1349
|
override handleNativeEvent(event: NativeUiEvent): void {
|
|
1350
|
+
if (event.type === "send") {
|
|
1351
|
+
// A send is its own prompt: never submit a stale draft for blank input,
|
|
1352
|
+
// and retain a displaced draft (including its attachments) for recall.
|
|
1353
|
+
if (!event.text.trim() || !this.nativeSendable) return;
|
|
1354
|
+
if (this.#pasteInFlight > 0) {
|
|
1355
|
+
this.#pendingInput.push(event);
|
|
1356
|
+
return;
|
|
1357
|
+
}
|
|
1358
|
+
this.clearDraftForRecall();
|
|
1359
|
+
this.setCollapsedText(event.text);
|
|
1360
|
+
this.submit();
|
|
1361
|
+
return;
|
|
1362
|
+
}
|
|
1255
1363
|
if (event.type !== "action") {
|
|
1256
1364
|
super.handleNativeEvent(event);
|
|
1257
1365
|
return;
|
|
@@ -1301,12 +1409,17 @@ export class CustomEditor extends Editor {
|
|
|
1301
1409
|
/** Called when the viewing header asks to view agent `id` ({@link MAIN_AGENT_ID}: the main session). */
|
|
1302
1410
|
onFocusAgent?: (id: string) => void;
|
|
1303
1411
|
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1412
|
+
#spaceHoldSnapshot: { revision: number; line: number; col: number } | undefined;
|
|
1413
|
+
|
|
1414
|
+
/** Configurable push-to-talk; set its `handler` to enable it. It is a text-composition gesture, so
|
|
1415
|
+
* it stays out of Vim's Normal/Visual modes (where the space bar is the `l` motion), away from an
|
|
1416
|
+
* open autocomplete menu, and out of a pending character jump (whose target may be the key). */
|
|
1307
1417
|
readonly spaceHold = new SpaceHoldGesture(
|
|
1308
|
-
count =>
|
|
1309
|
-
|
|
1418
|
+
count => {
|
|
1419
|
+
this.#spaceHoldSnapshot = undefined;
|
|
1420
|
+
if (count > 0) this.deleteBeforeCursor(count);
|
|
1421
|
+
},
|
|
1422
|
+
() => this.vimMode === "insert" && !this.isShowingAutocomplete() && !this.isJumpPending,
|
|
1310
1423
|
);
|
|
1311
1424
|
|
|
1312
1425
|
/** Custom key handlers from extensions and non-built-in app actions. */
|
|
@@ -1322,9 +1435,9 @@ export class CustomEditor extends Editor {
|
|
|
1322
1435
|
* dispatching them so a trailing `Enter` after `Cmd+V` can't submit before the image lands on
|
|
1323
1436
|
* `pendingImages` (Codex PR #3602 review). */
|
|
1324
1437
|
#pasteInFlight = 0;
|
|
1325
|
-
/** Input chunks deferred behind an in-flight paste,
|
|
1326
|
-
* count returns to zero. */
|
|
1327
|
-
#pendingInput: string[] = [];
|
|
1438
|
+
/** Input chunks and explicit prompts deferred behind an in-flight paste,
|
|
1439
|
+
* drained in FIFO order once the paste count returns to zero. */
|
|
1440
|
+
#pendingInput: (string | Extract<NativeUiEvent, { type: "send" }>)[] = [];
|
|
1328
1441
|
#actionKeys = new Map<ConfigurableEditorAction, KeyId[]>(
|
|
1329
1442
|
Object.entries(DEFAULT_ACTION_KEYS).map(([action, keys]) => [action as ConfigurableEditorAction, [...keys]]),
|
|
1330
1443
|
);
|
|
@@ -1396,10 +1509,17 @@ export class CustomEditor extends Editor {
|
|
|
1396
1509
|
#onPasteSettled = (): void => {
|
|
1397
1510
|
this.#pasteInFlight--;
|
|
1398
1511
|
if (this.#pasteInFlight > 0) return;
|
|
1399
|
-
|
|
1400
|
-
for (const chunk of drained) this.handleInput(chunk);
|
|
1512
|
+
this.#drainPendingInput();
|
|
1401
1513
|
};
|
|
1402
1514
|
|
|
1515
|
+
#drainPendingInput(): void {
|
|
1516
|
+
const drained = this.#pendingInput.splice(0);
|
|
1517
|
+
for (const input of drained) {
|
|
1518
|
+
if (typeof input === "string") this.handleInput(input);
|
|
1519
|
+
else this.handleNativeEvent(input);
|
|
1520
|
+
}
|
|
1521
|
+
}
|
|
1522
|
+
|
|
1403
1523
|
/** Track `promise` as an in-flight paste so subsequent `handleInput` calls queue behind it,
|
|
1404
1524
|
* then drain the queue once it settles. Codex PR #3602 review: without this, a trailing
|
|
1405
1525
|
* keystroke (Enter most painfully) in the same stdin read processes synchronously while the
|
|
@@ -1410,6 +1530,10 @@ export class CustomEditor extends Editor {
|
|
|
1410
1530
|
void promise.then(this.#onPasteSettled, this.#onPasteSettled);
|
|
1411
1531
|
}
|
|
1412
1532
|
|
|
1533
|
+
capturesInput(data: string): boolean {
|
|
1534
|
+
return this.spaceHold.shouldRoute(data);
|
|
1535
|
+
}
|
|
1536
|
+
|
|
1413
1537
|
override handleInput(data: string): void {
|
|
1414
1538
|
// Serialize behind any in-flight async paste so a trailing Enter / follow-up key can't
|
|
1415
1539
|
// submit before the clipboard image reaches `pendingImages` (Codex PR #3602 review).
|
|
@@ -1469,14 +1593,75 @@ export class CustomEditor extends Editor {
|
|
|
1469
1593
|
this.#collapseSkillTokens();
|
|
1470
1594
|
this.#collapseModelMentions();
|
|
1471
1595
|
// No async paste was started; drain the queued trailing bytes ourselves.
|
|
1472
|
-
|
|
1473
|
-
for (const chunk of drained) this.handleInput(chunk);
|
|
1596
|
+
this.#drainPendingInput();
|
|
1474
1597
|
return;
|
|
1475
1598
|
}
|
|
1476
1599
|
|
|
1477
1600
|
const parsedKey = parseKey(data);
|
|
1478
1601
|
const canonical = parsedKey !== undefined ? canonicalKeyId(parsedKey) : undefined;
|
|
1479
1602
|
|
|
1603
|
+
// Space-hold push-to-talk runs before editor shortcuts so a reserved binding can never
|
|
1604
|
+
// delete, move, submit, or invoke an app action while the gesture is enabled.
|
|
1605
|
+
const spaceHoldText = getSpaceHoldText(data, canonical);
|
|
1606
|
+
const priorSnapshot = this.#spaceHoldSnapshot;
|
|
1607
|
+
if (priorSnapshot) {
|
|
1608
|
+
const cursor = this.getCursor();
|
|
1609
|
+
if (
|
|
1610
|
+
this.textRevision !== priorSnapshot.revision ||
|
|
1611
|
+
cursor.line !== priorSnapshot.line ||
|
|
1612
|
+
cursor.col !== priorSnapshot.col
|
|
1613
|
+
) {
|
|
1614
|
+
this.spaceHold.process(undefined);
|
|
1615
|
+
this.#spaceHoldSnapshot = undefined;
|
|
1616
|
+
}
|
|
1617
|
+
}
|
|
1618
|
+
|
|
1619
|
+
switch (this.spaceHold.process(canonical, spaceHoldText?.length ?? 0)) {
|
|
1620
|
+
case "type": {
|
|
1621
|
+
const text = spaceHoldText!;
|
|
1622
|
+
const beforeCursor = this.getCursor();
|
|
1623
|
+
const beforeLine = this.getLines()[beforeCursor.line] ?? "";
|
|
1624
|
+
const beforeRevision = this.textRevision;
|
|
1625
|
+
this.typeCharacter(text);
|
|
1626
|
+
this.#collapseSkillTokens();
|
|
1627
|
+
this.#collapseModelMentions();
|
|
1628
|
+
this.#normalizeQueuePrefix(hadBareQueuePrefix);
|
|
1629
|
+
|
|
1630
|
+
const afterCursor = this.getCursor();
|
|
1631
|
+
const afterLine = this.getLines()[afterCursor.line] ?? "";
|
|
1632
|
+
const noInsertion =
|
|
1633
|
+
this.textRevision === beforeRevision &&
|
|
1634
|
+
afterCursor.line === beforeCursor.line &&
|
|
1635
|
+
afterCursor.col === beforeCursor.col &&
|
|
1636
|
+
afterLine === beforeLine;
|
|
1637
|
+
const literalInsertion =
|
|
1638
|
+
afterCursor.line === beforeCursor.line &&
|
|
1639
|
+
afterCursor.col === beforeCursor.col + text.length &&
|
|
1640
|
+
afterLine === beforeLine.slice(0, beforeCursor.col) + text + beforeLine.slice(beforeCursor.col);
|
|
1641
|
+
if (noInsertion) {
|
|
1642
|
+
this.spaceHold.recordTyped(0);
|
|
1643
|
+
} else if (literalInsertion) {
|
|
1644
|
+
this.spaceHold.recordTyped(text.length);
|
|
1645
|
+
} else {
|
|
1646
|
+
// Typing hooks rewrote the candidate (e.g. autocorrect/inline replacement).
|
|
1647
|
+
// Commit that edit and start a fresh cadence rather than deleting its suffix later.
|
|
1648
|
+
this.spaceHold.process(undefined);
|
|
1649
|
+
this.#spaceHoldSnapshot = undefined;
|
|
1650
|
+
return;
|
|
1651
|
+
}
|
|
1652
|
+
const cursor = this.getCursor();
|
|
1653
|
+
this.#spaceHoldSnapshot = {
|
|
1654
|
+
revision: this.textRevision,
|
|
1655
|
+
line: cursor.line,
|
|
1656
|
+
col: cursor.col,
|
|
1657
|
+
};
|
|
1658
|
+
return;
|
|
1659
|
+
}
|
|
1660
|
+
case "swallow":
|
|
1661
|
+
return;
|
|
1662
|
+
}
|
|
1663
|
+
this.#spaceHoldSnapshot = undefined;
|
|
1664
|
+
|
|
1480
1665
|
// Left-arrow on an empty editor: surface for the agent-hub double-tap
|
|
1481
1666
|
// gesture. Plain "left" only — modified arrows and any in-text cursor
|
|
1482
1667
|
// movement fall through to normal handling.
|
|
@@ -1485,24 +1670,16 @@ export class CustomEditor extends Editor {
|
|
|
1485
1670
|
return;
|
|
1486
1671
|
}
|
|
1487
1672
|
|
|
1488
|
-
// Space-hold push-to-talk: a sustained space bar starts/stops STT instead of typing spaces.
|
|
1489
|
-
switch (this.spaceHold.process(canonical === "space")) {
|
|
1490
|
-
case "type":
|
|
1491
|
-
this.#forwardInput(data);
|
|
1492
|
-
return;
|
|
1493
|
-
case "swallow":
|
|
1494
|
-
return;
|
|
1495
|
-
}
|
|
1496
|
-
|
|
1497
1673
|
// One union probe decides whether any per-action interception below can
|
|
1498
1674
|
// match — plain typing then skips the ~20 per-action set lookups per key.
|
|
1499
1675
|
if (
|
|
1500
1676
|
canonical !== undefined &&
|
|
1501
1677
|
(this.#actionMatchKeyUnion.has(canonical) || this.#customMatchKeys.has(canonical))
|
|
1502
1678
|
) {
|
|
1503
|
-
//
|
|
1679
|
+
// Serialize configured clipboard paste just like bracketed image paste:
|
|
1680
|
+
// explicit sends and subsequent keys must wait for its attachments.
|
|
1504
1681
|
if (this.#matchesAction(canonical, "app.clipboard.pasteImage") && this.onPasteImage) {
|
|
1505
|
-
|
|
1682
|
+
this.#trackAsyncPaste(Promise.resolve(this.onPasteImage()));
|
|
1506
1683
|
return;
|
|
1507
1684
|
}
|
|
1508
1685
|
|