@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
|
@@ -212,11 +212,7 @@ function questionTabLabel(question: ExtensionAskDialogQuestion, index: number):
|
|
|
212
212
|
}
|
|
213
213
|
|
|
214
214
|
function wrapQuestionTitle(question: ExtensionAskDialogQuestion, width: number): string[] {
|
|
215
|
-
|
|
216
|
-
const questionText = renderInlineMarkdown(replaceTabs(sanitizeCarriageReturns(question.question)), mdTheme, t =>
|
|
217
|
-
theme.fg("text", t),
|
|
218
|
-
);
|
|
219
|
-
return wrapTextWithAnsi(questionText, Math.max(1, width));
|
|
215
|
+
return renderPreviewContent(sanitizeCarriageReturns(question.question), Math.max(1, width), "text");
|
|
220
216
|
}
|
|
221
217
|
|
|
222
218
|
function renderQuestionTitle(question: ExtensionAskDialogQuestion, width: number, maxRows = MAX_HEADER_ROWS): string[] {
|
|
@@ -299,10 +295,10 @@ function splitPreviewSegments(preview: string): PreviewSegment[] {
|
|
|
299
295
|
return segments;
|
|
300
296
|
}
|
|
301
297
|
|
|
302
|
-
function renderPreviewContent(preview: string, width: number): string[] {
|
|
298
|
+
function renderPreviewContent(preview: string, width: number, textColor: "muted" | "text" = "muted"): string[] {
|
|
303
299
|
const out: string[] = [];
|
|
304
300
|
const mdTheme = getMarkdownTheme();
|
|
305
|
-
const accentStyle = { color: (text: string) => theme.fg(
|
|
301
|
+
const accentStyle = { color: (text: string) => theme.fg(textColor, text) };
|
|
306
302
|
for (const segment of splitPreviewSegments(preview)) {
|
|
307
303
|
if (segment.kind === "code") {
|
|
308
304
|
const highlighted = highlightCode(segment.text, segment.language);
|
|
@@ -715,8 +711,11 @@ export class AskDialogComponent implements Component {
|
|
|
715
711
|
}
|
|
716
712
|
|
|
717
713
|
/**
|
|
718
|
-
*
|
|
719
|
-
*
|
|
714
|
+
* In the dock, in the composer's place and framed as the composer is: a
|
|
715
|
+
* `col` with the prompt editor's root role (`zeta.editor`), so the terminal
|
|
716
|
+
* gives it the composer's insets and spacing. Not a modal `overlay` sheet:
|
|
717
|
+
* one anchored at the bottom covered the transcript rows that explain the
|
|
718
|
+
* question and blocked scrolling until the question was answered.
|
|
720
719
|
*/
|
|
721
720
|
describe(cx: DescribeContext): NativeNode {
|
|
722
721
|
const inputGuard = this.options.inputGuard;
|
|
@@ -732,13 +731,7 @@ export class AskDialogComponent implements Component {
|
|
|
732
731
|
if (this.#isSubmitTab()) this.#describeSubmitBody(children);
|
|
733
732
|
else this.#describeQuestionBody(children);
|
|
734
733
|
children.push(this.#describeActions(blocked));
|
|
735
|
-
|
|
736
|
-
"overlay",
|
|
737
|
-
{ role: "zeta.overlay.ask", anchor: "bottom", size: "md", modal: true },
|
|
738
|
-
[col(children, { gap: "md" })],
|
|
739
|
-
"sheet",
|
|
740
|
-
);
|
|
741
|
-
this.#native = col([sheet]);
|
|
734
|
+
this.#native = col(children, { role: "zeta.editor", gap: "md" });
|
|
742
735
|
this.#nativeBlocked = blocked;
|
|
743
736
|
return this.#native;
|
|
744
737
|
}
|
|
@@ -1141,7 +1134,7 @@ export class AskDialogComponent implements Component {
|
|
|
1141
1134
|
const action = question?.multi
|
|
1142
1135
|
? `${formatKeyHint("space")} toggle · ${enter} ${enterAction}`
|
|
1143
1136
|
: `${enter} select · ${formatKeyHint("n")} note`;
|
|
1144
|
-
const tabs = this.#hasSubmitTab() ? ` · ${formatKeyHints(["tab", "left", "right"])}` : "";
|
|
1137
|
+
const tabs = this.#hasSubmitTab() ? ` · ${formatKeyHints(["tab", "left", "right"])} question` : "";
|
|
1145
1138
|
const expand = this.#expandHint();
|
|
1146
1139
|
if (this.#questionCanPage && indicator) {
|
|
1147
1140
|
const pageKeys = editorKeys("tui.select.pageUp", "tui.select.pageDown");
|
|
@@ -29,15 +29,15 @@ import type { SpaceHoldHandler } from "../space-hold";
|
|
|
29
29
|
import { sanitizeErrorLine } from "../chrome/error-block";
|
|
30
30
|
import { sanitizeDisplayLine, sanitizeDisplayText } from "./extensions/display-text";
|
|
31
31
|
import { editorKey, editorKeys, keyHint, rawKeyHint } from "../chrome/keybinding-hints";
|
|
32
|
-
import { formatKeyHint } from "../app-keybindings";
|
|
32
|
+
import { formatKeyHint, type KeyId } from "../app-keybindings";
|
|
33
33
|
import { bottomBorder, row, topBorder } from "../chrome/overlay-box";
|
|
34
34
|
import { padToWidth } from "../render/utils";
|
|
35
35
|
import { SplitPane } from "../components/layout/split-pane";
|
|
36
36
|
import { clampSelection, contentRowWidth, padLinesToHeight, renderScrollableList } from "../chrome/selector-helpers";
|
|
37
|
-
import type { TspSpan } from "@linxiraos/pi-wire";
|
|
38
|
-
import type { NativeChild, NativeNode, NativeUiEvent } from "../native/node";
|
|
37
|
+
import type { TspScrollBy, TspSpan } from "@linxiraos/pi-wire";
|
|
38
|
+
import type { NativeChild, NativeNode, NativeScroll, NativeUiEvent } from "../native/node";
|
|
39
39
|
import { col, md, node, span, text } from "../native/describe";
|
|
40
|
-
import { actionHint, hintsRow, type NativeHint, statusHintsRow } from "../native/overlay";
|
|
40
|
+
import { actionBar, actionButton, actionHint, hintsRow, type NativeHint, statusHintsRow } from "../native/overlay";
|
|
41
41
|
|
|
42
42
|
interface BtwHistoryPanelOptions {
|
|
43
43
|
records: readonly BtwHistoryRecord[];
|
|
@@ -46,10 +46,17 @@ interface BtwHistoryPanelOptions {
|
|
|
46
46
|
onCancel: (record: BtwHistoryRecord) => void;
|
|
47
47
|
canFollowUp?: (record: BtwHistoryRecord) => boolean;
|
|
48
48
|
onFollowUp?: (record: BtwHistoryRecord, question: string, signal: AbortSignal) => Promise<boolean>;
|
|
49
|
-
/**
|
|
49
|
+
/** Configured push-to-talk keys for a follow-up composer. */
|
|
50
|
+
spaceHoldKeys?: readonly KeyId[];
|
|
50
51
|
spaceHold?: (input: Input) => SpaceHoldHandler;
|
|
51
52
|
requestRender: () => void;
|
|
52
53
|
getHeight: () => number;
|
|
54
|
+
/**
|
|
55
|
+
* Esc closes the panel even while the selected answer runs (it keeps
|
|
56
|
+
* streaming in the background) and `x` cancels it, instead of Esc cancelling
|
|
57
|
+
* first. For a host with no inline panel to fall back to (Tern).
|
|
58
|
+
*/
|
|
59
|
+
escapeHides?: boolean;
|
|
53
60
|
}
|
|
54
61
|
|
|
55
62
|
interface FollowUpComposer {
|
|
@@ -78,6 +85,7 @@ interface BtwHistoryNativeMemo {
|
|
|
78
85
|
followUpPending: boolean;
|
|
79
86
|
canFollowUp: boolean;
|
|
80
87
|
copied: boolean;
|
|
88
|
+
scroll: NativeScroll | undefined;
|
|
81
89
|
node: NativeNode;
|
|
82
90
|
}
|
|
83
91
|
|
|
@@ -108,6 +116,8 @@ export class BtwHistoryPanel implements Component, Focusable {
|
|
|
108
116
|
#lastCopiedId: string | undefined;
|
|
109
117
|
#lastCopiedText: string | undefined;
|
|
110
118
|
#native: BtwHistoryNativeMemo | undefined;
|
|
119
|
+
/** The last answer-pane scroll key, forwarded natively (see {@link NativeNode.scroll}). */
|
|
120
|
+
#scroll: NativeScroll | undefined;
|
|
111
121
|
readonly #detail = new ScrollView([], {
|
|
112
122
|
height: 1,
|
|
113
123
|
scrollbar: "auto",
|
|
@@ -159,6 +169,7 @@ export class BtwHistoryPanel implements Component, Focusable {
|
|
|
159
169
|
this.#options = options;
|
|
160
170
|
this.#records = options.records;
|
|
161
171
|
this.#selectedId = options.records[0]?.id;
|
|
172
|
+
if (options.records.length === 1) this.#focus = "answer";
|
|
162
173
|
}
|
|
163
174
|
|
|
164
175
|
get focused(): boolean {
|
|
@@ -187,6 +198,8 @@ export class BtwHistoryPanel implements Component, Focusable {
|
|
|
187
198
|
this.#followLatest = false;
|
|
188
199
|
this.#detail.scrollToTop();
|
|
189
200
|
}
|
|
201
|
+
// One side question has nothing to pick: its answer keeps the arrow keys.
|
|
202
|
+
if (records.length === 1) this.#focus = "answer";
|
|
190
203
|
this.#options.requestRender();
|
|
191
204
|
}
|
|
192
205
|
|
|
@@ -247,9 +260,30 @@ export class BtwHistoryPanel implements Component, Focusable {
|
|
|
247
260
|
return true;
|
|
248
261
|
}
|
|
249
262
|
|
|
263
|
+
/** Select a record with its answer pane focused, read from the top (a just-asked /btw). */
|
|
264
|
+
showRecord(recordId: string): void {
|
|
265
|
+
const index = this.#records.findIndex(record => record.id === recordId);
|
|
266
|
+
if (index === -1) return;
|
|
267
|
+
this.#select(index);
|
|
268
|
+
this.#focus = "answer";
|
|
269
|
+
this.#options.requestRender();
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Jump the answer pane to its bottom once (opening or sending a follow-up):
|
|
274
|
+
* text mode keeps following the newest row at render until a scroll key;
|
|
275
|
+
* natively the sheet body scrolls to its end. Tern reports no wheel scroll,
|
|
276
|
+
* so streamed updates never re-pin it there and a reader is never yanked.
|
|
277
|
+
*/
|
|
278
|
+
#jumpToEnd(): void {
|
|
279
|
+
this.#followLatest = true;
|
|
280
|
+
this.#scroll = { by: "end", n: (this.#scroll?.n ?? 0) + 1 };
|
|
281
|
+
}
|
|
282
|
+
|
|
250
283
|
#openComposer(record: BtwHistoryRecord): void {
|
|
251
284
|
const input = new Input();
|
|
252
285
|
input.prompt = theme.fg("accent", "Follow up: ");
|
|
286
|
+
if (this.#options.spaceHoldKeys !== undefined) input.spaceHold.keys = this.#options.spaceHoldKeys;
|
|
253
287
|
input.spaceHold.handler = this.#options.spaceHold?.(input);
|
|
254
288
|
const composer: FollowUpComposer = { recordId: record.id, input, abortController: new AbortController() };
|
|
255
289
|
input.onEscape = () => {
|
|
@@ -261,6 +295,8 @@ export class BtwHistoryPanel implements Component, Focusable {
|
|
|
261
295
|
};
|
|
262
296
|
this.#composer = composer;
|
|
263
297
|
this.#focus = "answer";
|
|
298
|
+
// Following up reads from the newest turn, right above the composer.
|
|
299
|
+
this.#jumpToEnd();
|
|
264
300
|
this.#options.requestRender();
|
|
265
301
|
}
|
|
266
302
|
|
|
@@ -286,7 +322,7 @@ export class BtwHistoryPanel implements Component, Focusable {
|
|
|
286
322
|
this.#composer = undefined;
|
|
287
323
|
this.#selectedId = composer.recordId;
|
|
288
324
|
this.#focus = "answer";
|
|
289
|
-
this.#
|
|
325
|
+
this.#jumpToEnd();
|
|
290
326
|
} else {
|
|
291
327
|
composer.notice = `Follow-up was not started. Your draft is kept; ${editorKey("tui.input.submit")} to retry.`;
|
|
292
328
|
}
|
|
@@ -300,6 +336,11 @@ export class BtwHistoryPanel implements Component, Focusable {
|
|
|
300
336
|
}
|
|
301
337
|
}
|
|
302
338
|
|
|
339
|
+
/** Only an active follow-up composer can claim its configured push-to-talk keys. */
|
|
340
|
+
capturesInput(data: string): boolean {
|
|
341
|
+
return this.#composer?.input.capturesInput(data) ?? false;
|
|
342
|
+
}
|
|
343
|
+
|
|
303
344
|
/** Enhanced clipboard pastes belong only to the active composer. */
|
|
304
345
|
pasteText(text: string): void {
|
|
305
346
|
if (!this.#composer) return;
|
|
@@ -314,11 +355,17 @@ export class BtwHistoryPanel implements Component, Focusable {
|
|
|
314
355
|
return;
|
|
315
356
|
}
|
|
316
357
|
const record = this.#selected();
|
|
358
|
+
const running = record !== undefined && getBtwLatestTurn(record).status === "running" ? record : undefined;
|
|
359
|
+
const hides = this.#options.escapeHides === true;
|
|
317
360
|
if (matchesSelectCancel(data) || matchesKey(data, "escape")) {
|
|
318
|
-
if (
|
|
361
|
+
if (running && !hides) this.#options.onCancel(running);
|
|
319
362
|
else this.#options.onClose();
|
|
320
363
|
return;
|
|
321
364
|
}
|
|
365
|
+
if (hides && matchesKey(data, "x")) {
|
|
366
|
+
if (running) this.#options.onCancel(running);
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
322
369
|
if (matchesKey(data, "f") || matchesKey(data, "enter")) {
|
|
323
370
|
if (record) this.openFollowUp(record.id);
|
|
324
371
|
return;
|
|
@@ -335,11 +382,12 @@ export class BtwHistoryPanel implements Component, Focusable {
|
|
|
335
382
|
matchesKey(data, "ctrl+_") ||
|
|
336
383
|
data === String.fromCharCode(31)
|
|
337
384
|
) {
|
|
338
|
-
|
|
385
|
+
// One side question has nothing to pick: its answer keeps the arrow keys.
|
|
386
|
+
if (this.#records.length > 1) this.#focus = this.#focus === "list" ? "answer" : "list";
|
|
339
387
|
} else if (matchesKey(data, "right")) {
|
|
340
388
|
this.#focus = "answer";
|
|
341
389
|
} else if (matchesKey(data, "left")) {
|
|
342
|
-
this.#focus = "list";
|
|
390
|
+
if (this.#records.length > 1) this.#focus = "list";
|
|
343
391
|
} else if (this.#focus === "list") {
|
|
344
392
|
const index = this.#selectedIndex();
|
|
345
393
|
if (matchesSelectUp(data)) this.#select(index - 1);
|
|
@@ -350,11 +398,25 @@ export class BtwHistoryPanel implements Component, Focusable {
|
|
|
350
398
|
else if (matchesKey(data, "end")) this.#select(this.#records.length - 1);
|
|
351
399
|
else return;
|
|
352
400
|
} else {
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
401
|
+
const by: TspScrollBy | undefined = matchesSelectUp(data)
|
|
402
|
+
? "line-up"
|
|
403
|
+
: matchesSelectDown(data)
|
|
404
|
+
? "line-down"
|
|
405
|
+
: matchesSelectPageUp(data)
|
|
406
|
+
? "page-up"
|
|
407
|
+
: matchesSelectPageDown(data)
|
|
408
|
+
? "page-down"
|
|
409
|
+
: matchesKey(data, "home")
|
|
410
|
+
? "start"
|
|
411
|
+
: matchesKey(data, "end")
|
|
412
|
+
? "end"
|
|
413
|
+
: undefined;
|
|
414
|
+
if (by === "line-up") this.#detail.scroll(-1);
|
|
415
|
+
else if (by === "line-down") this.#detail.scroll(1);
|
|
416
|
+
else if (by === "page-up") this.#detail.page(-1);
|
|
417
|
+
else if (by === "page-down") this.#detail.page(1);
|
|
357
418
|
else if (!this.#detail.handleScrollKey(data)) return;
|
|
419
|
+
if (by) this.#scroll = { by, n: (this.#scroll?.n ?? 0) + 1 };
|
|
358
420
|
this.#followLatest = matchesKey(data, "end");
|
|
359
421
|
}
|
|
360
422
|
this.#options.requestRender();
|
|
@@ -378,31 +440,39 @@ export class BtwHistoryPanel implements Component, Focusable {
|
|
|
378
440
|
memo.notice === composer?.notice &&
|
|
379
441
|
memo.followUpPending === this.#followUpPending &&
|
|
380
442
|
memo.canFollowUp === canFollowUp &&
|
|
381
|
-
memo.copied === copied
|
|
443
|
+
memo.copied === copied &&
|
|
444
|
+
memo.scroll === this.#scroll
|
|
382
445
|
) {
|
|
383
446
|
return memo.node;
|
|
384
447
|
}
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
448
|
+
// Plain columns, not `section`s: Tern draws a section as a foldable `##` heading,
|
|
449
|
+
// and a flex item's min-content width (a long code line in the answer) would
|
|
450
|
+
// crush the list pane. The answer pane may shrink to nothing; the list may not.
|
|
451
|
+
// With one side question the list and the pane labels are noise: the answer alone.
|
|
452
|
+
const showList = this.#records.length > 1;
|
|
453
|
+
const detail = this.#describeDetail(record, copied);
|
|
454
|
+
const answerPane: NativeNode = {
|
|
455
|
+
...node(
|
|
456
|
+
"col",
|
|
457
|
+
{ gap: "sm", grow: 1, basis: 0, min: { w: 0 } },
|
|
458
|
+
showList ? [text(this.#describeFocusLabel("answer"), { truncate: "end", lines: 1 }), detail] : [detail],
|
|
459
|
+
"details",
|
|
460
|
+
),
|
|
461
|
+
// Natively the sheet body is the scroller; arrow/page keys on the answer pane move it.
|
|
462
|
+
scroll: this.#scroll,
|
|
463
|
+
};
|
|
464
|
+
const panes: NativeChild[] = showList
|
|
465
|
+
? [
|
|
390
466
|
node(
|
|
391
|
-
"
|
|
392
|
-
{
|
|
393
|
-
[this.#describeList()],
|
|
467
|
+
"col",
|
|
468
|
+
{ gap: "sm", shrink: 0, basis: 0.3, max: { w: "40ch" } },
|
|
469
|
+
[text(this.#describeFocusLabel("list"), { truncate: "end", lines: 1 }), this.#describeList()],
|
|
394
470
|
"history",
|
|
395
471
|
),
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
"details",
|
|
401
|
-
),
|
|
402
|
-
],
|
|
403
|
-
"body",
|
|
404
|
-
),
|
|
405
|
-
];
|
|
472
|
+
answerPane,
|
|
473
|
+
]
|
|
474
|
+
: [answerPane];
|
|
475
|
+
const children: NativeChild[] = [node("row", { gap: "lg", align: "start" }, panes, "body")];
|
|
406
476
|
if (composer) {
|
|
407
477
|
composer.input.focused = this.#focused;
|
|
408
478
|
const lines: NativeChild[] = [
|
|
@@ -415,6 +485,13 @@ export class BtwHistoryPanel implements Component, Focusable {
|
|
|
415
485
|
children.push(node("col", undefined, lines, "composer"));
|
|
416
486
|
}
|
|
417
487
|
children.push({ ...this.#describeFooter(record, canFollowUp, copied), key: "footer" });
|
|
488
|
+
// Clicking Close always dismisses the sheet; the Esc keycap only shows where Esc does too.
|
|
489
|
+
const running = record !== undefined && getBtwLatestTurn(record).status === "running";
|
|
490
|
+
const escapeCloses = !composer && !(running && !this.#options.escapeHides);
|
|
491
|
+
children.push({
|
|
492
|
+
...actionBar([null, actionButton("Close", "close", escapeCloses ? { keys: "escape" } : undefined)]),
|
|
493
|
+
key: "actions",
|
|
494
|
+
});
|
|
418
495
|
// The sheet is the frame (`nativeOverlay`): a borderless column.
|
|
419
496
|
const described = col(children, { gap: "md" });
|
|
420
497
|
this.#native = {
|
|
@@ -427,12 +504,17 @@ export class BtwHistoryPanel implements Component, Focusable {
|
|
|
427
504
|
followUpPending: this.#followUpPending,
|
|
428
505
|
canFollowUp,
|
|
429
506
|
copied,
|
|
507
|
+
scroll: this.#scroll,
|
|
430
508
|
node: described,
|
|
431
509
|
};
|
|
432
510
|
return described;
|
|
433
511
|
}
|
|
434
512
|
|
|
435
513
|
handleNativeEvent(event: NativeUiEvent): void {
|
|
514
|
+
if (event.type === "action" && event.act === "close") {
|
|
515
|
+
this.#options.onClose();
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
436
518
|
if ((event.type !== "select" && event.type !== "activate") || this.#composer) return;
|
|
437
519
|
const index = this.#records.findIndex(record => record.id === event.item);
|
|
438
520
|
if (index === -1) return;
|
|
@@ -492,10 +574,21 @@ export class BtwHistoryPanel implements Component, Focusable {
|
|
|
492
574
|
const answer = turn.answer.trim()
|
|
493
575
|
? md(sanitizeDisplayText(turn.answer), { stream: turn.status === "running" })
|
|
494
576
|
: text([span(turn.status === "running" ? "Waiting for response…" : "No answer text.", "dim")]);
|
|
577
|
+
// Plain labels, not `section`s (Tern draws those as foldable `##` headings);
|
|
578
|
+
// the question stays markdown so its inline code and links render.
|
|
495
579
|
const children: NativeChild[] = [
|
|
496
|
-
text(
|
|
497
|
-
|
|
498
|
-
|
|
580
|
+
text(
|
|
581
|
+
[
|
|
582
|
+
span("Question", "accent strong"),
|
|
583
|
+
span(" · ", "dim"),
|
|
584
|
+
span(status.label, status.color),
|
|
585
|
+
span(` · ${this.#dateFormat.format(turn.createdAt)}`, "dim"),
|
|
586
|
+
],
|
|
587
|
+
{ wrap: "word" },
|
|
588
|
+
),
|
|
589
|
+
md(sanitizeDisplayText(turn.question)),
|
|
590
|
+
text([span("Answer", "accent strong")]),
|
|
591
|
+
answer,
|
|
499
592
|
];
|
|
500
593
|
if (turn.error) children.push(text([span(sanitizeErrorLine(turn.error), "error")], { wrap: "word" }));
|
|
501
594
|
if (turn.status === "interrupted") children.push(text([span("Not resumed in this view.", "muted")]));
|
|
@@ -510,9 +603,11 @@ export class BtwHistoryPanel implements Component, Focusable {
|
|
|
510
603
|
]);
|
|
511
604
|
}
|
|
512
605
|
const latest = record ? getBtwLatestTurn(record) : undefined;
|
|
606
|
+
const running = latest?.status === "running";
|
|
513
607
|
const hints: (NativeHint | undefined)[] = [
|
|
514
|
-
actionHint("tui.select.cancel",
|
|
515
|
-
{ keys: ["
|
|
608
|
+
actionHint("tui.select.cancel", running && !this.#options.escapeHides ? "cancel" : "close"),
|
|
609
|
+
running && this.#options.escapeHides ? { keys: ["x"], label: "cancel" } : undefined,
|
|
610
|
+
this.#records.length > 1 ? { keys: ["tab", "ctrl+/"], label: "switch pane" } : undefined,
|
|
516
611
|
actionHint(["tui.select.up", "tui.select.down"], this.#focus === "list" ? "select" : "scroll"),
|
|
517
612
|
];
|
|
518
613
|
if (canFollowUp) hints.push({ keys: ["f", "enter"], label: "follow up" });
|
|
@@ -689,14 +784,16 @@ export class BtwHistoryPanel implements Component, Focusable {
|
|
|
689
784
|
const record = this.#selected();
|
|
690
785
|
const composer = this.#composer;
|
|
691
786
|
const latest = record ? getBtwLatestTurn(record) : undefined;
|
|
787
|
+
const running = latest?.status === "running";
|
|
692
788
|
const actions = composer
|
|
693
789
|
? [
|
|
694
790
|
keyHint("tui.input.submit", this.#followUpPending ? "starting…" : "send"),
|
|
695
791
|
keyHint("tui.select.cancel", "cancel"),
|
|
696
792
|
]
|
|
697
793
|
: [
|
|
698
|
-
keyHint("tui.select.cancel",
|
|
699
|
-
rawKeyHint(
|
|
794
|
+
keyHint("tui.select.cancel", running && !this.#options.escapeHides ? "cancel" : "close"),
|
|
795
|
+
...(running && this.#options.escapeHides ? [rawKeyHint("x", "cancel")] : []),
|
|
796
|
+
...(this.#records.length > 1 ? [rawKeyHint(["tab", "ctrl+/"], "switch pane")] : []),
|
|
700
797
|
];
|
|
701
798
|
if (!composer) {
|
|
702
799
|
if (record && this.#canFollowUp(record)) actions.push(rawKeyHint(["f", "enter"], "follow up"));
|
|
@@ -31,6 +31,7 @@ export interface CopySelectorDeps {
|
|
|
31
31
|
cwd: string;
|
|
32
32
|
hideThinkingBlock?: () => boolean;
|
|
33
33
|
proseOnlyThinking?: () => boolean;
|
|
34
|
+
expandThinkingBlocks?: () => boolean;
|
|
34
35
|
linkTargets?: ReadonlyMap<string, string>;
|
|
35
36
|
requestRender: () => void;
|
|
36
37
|
/** Replaces the "Copy" header when the picker is reused for another purpose. */
|
|
@@ -274,6 +274,7 @@ export interface CopySelectorDeps {
|
|
|
274
274
|
cwd: string;
|
|
275
275
|
hideThinkingBlock?: () => boolean;
|
|
276
276
|
proseOnlyThinking?: () => boolean;
|
|
277
|
+
expandThinkingBlocks?: () => boolean;
|
|
277
278
|
linkTargets?: ReadonlyMap<string, string>;
|
|
278
279
|
requestRender: () => void;
|
|
279
280
|
/** Replaces the "Copy" header when the picker is reused for another purpose. */
|
|
@@ -326,6 +327,12 @@ interface ControlRegion {
|
|
|
326
327
|
end: number;
|
|
327
328
|
}
|
|
328
329
|
|
|
330
|
+
/** A block's preview rows (highlighted, width-cut, plus the "more lines" row) and its full line count. */
|
|
331
|
+
interface BlockPreview {
|
|
332
|
+
rows: string[];
|
|
333
|
+
lineCount: number;
|
|
334
|
+
}
|
|
335
|
+
|
|
329
336
|
export class CopySelectorComponent implements Component {
|
|
330
337
|
#builder: ChatTranscriptBuilder;
|
|
331
338
|
#browser: TranscriptBrowser;
|
|
@@ -347,6 +354,8 @@ export class CopySelectorComponent implements Component {
|
|
|
347
354
|
|
|
348
355
|
/** Last described root and the state it was built from. */
|
|
349
356
|
#native: { memo: string; targets: OutlineTarget[]; blocks: CopyBlock[] | undefined; node: NativeNode } | undefined;
|
|
357
|
+
/** ANSI block previews (syntax highlight + width cut) of the exploded turn. */
|
|
358
|
+
#blockPreviews: { blocks: CopyBlock[]; inner: number; previews: readonly BlockPreview[] } | undefined;
|
|
350
359
|
/** Transcript list items, rebuilt only when the replayed targets change. */
|
|
351
360
|
#nativeItems: { targets: OutlineTarget[]; truncated: boolean; items: NativeNode[] } | undefined;
|
|
352
361
|
/** Timeline picker items of the replayed targets. */
|
|
@@ -379,6 +388,7 @@ export class CopySelectorComponent implements Component {
|
|
|
379
388
|
cwd: this.deps.cwd,
|
|
380
389
|
hideThinkingBlock: this.deps.hideThinkingBlock,
|
|
381
390
|
proseOnlyThinking: this.deps.proseOnlyThinking,
|
|
391
|
+
expandThinkingBlocks: this.deps.expandThinkingBlocks,
|
|
382
392
|
linkTargets: this.deps.linkTargets,
|
|
383
393
|
requestRender: this.deps.requestRender,
|
|
384
394
|
});
|
|
@@ -412,6 +422,7 @@ export class CopySelectorComponent implements Component {
|
|
|
412
422
|
}
|
|
413
423
|
|
|
414
424
|
invalidate(): void {
|
|
425
|
+
this.#blockPreviews = undefined;
|
|
415
426
|
this.#builder.container.invalidate();
|
|
416
427
|
this.#browser.invalidate();
|
|
417
428
|
}
|
|
@@ -943,18 +954,13 @@ export class CopySelectorComponent implements Component {
|
|
|
943
954
|
*/
|
|
944
955
|
#composeBlocks(blocks: CopyBlock[], columnWidth: number, lineOffset: number): ComposedColumn {
|
|
945
956
|
const inner = Math.max(10, columnWidth - 4);
|
|
957
|
+
const previews = this.#previewBlocks(blocks, inner);
|
|
946
958
|
const lines: string[] = [];
|
|
947
959
|
let selStart = -1;
|
|
948
960
|
let selEnd = -1;
|
|
949
961
|
for (let index = 0; index < blocks.length; index++) {
|
|
950
962
|
const block = blocks[index]!;
|
|
951
|
-
const
|
|
952
|
-
const shown = raw.slice(0, BLOCK_PREVIEW_LINES);
|
|
953
|
-
const styled = block.language ? highlightCode(shown.join("\n"), block.language) : shown;
|
|
954
|
-
const rows = styled.map(row => truncateToWidth(replaceTabs(row), inner));
|
|
955
|
-
if (raw.length > shown.length) {
|
|
956
|
-
rows.push(theme.fg("dim", `… +${raw.length - shown.length} more lines`));
|
|
957
|
-
}
|
|
963
|
+
const { rows, lineCount } = previews[index]!;
|
|
958
964
|
const selected = index === this.#blockSelected;
|
|
959
965
|
const captionColor: ThemeColor = selected ? OUTLINE_COLOR : "dim";
|
|
960
966
|
const controls: Array<{ action: ControlRegion["action"]; text: string }> = [
|
|
@@ -963,7 +969,7 @@ export class CopySelectorComponent implements Component {
|
|
|
963
969
|
if (block.href && this.deps.onOpen) controls.push({ action: "open", text: `${theme.cmd.share} open` });
|
|
964
970
|
const controlsWidth = controls.reduce((sum, control) => sum + visibleWidth(control.text) + 2, 0);
|
|
965
971
|
const summary = truncateToWidth(
|
|
966
|
-
`${index + 1}/${blocks.length}${theme.sep.dot}${block.label}${theme.sep.dot}${
|
|
972
|
+
`${index + 1}/${blocks.length}${theme.sep.dot}${block.label}${theme.sep.dot}${lineCount} line${lineCount === 1 ? "" : "s"}`,
|
|
967
973
|
Math.max(4, inner - controlsWidth),
|
|
968
974
|
);
|
|
969
975
|
// Caption: two-space gutter, summary, then the controls, each preceded by two spaces.
|
|
@@ -996,6 +1002,24 @@ export class CopySelectorComponent implements Component {
|
|
|
996
1002
|
lines.push("");
|
|
997
1003
|
return { lines, selStart, selEnd };
|
|
998
1004
|
}
|
|
1005
|
+
|
|
1006
|
+
/** Highlighted, width-cut previews of each block; rebuilt when the block set or width changes. */
|
|
1007
|
+
#previewBlocks(blocks: CopyBlock[], inner: number): readonly BlockPreview[] {
|
|
1008
|
+
const cached = this.#blockPreviews;
|
|
1009
|
+
if (cached?.blocks === blocks && cached.inner === inner) return cached.previews;
|
|
1010
|
+
const previews = blocks.map(block => {
|
|
1011
|
+
const raw = block.content.split("\n");
|
|
1012
|
+
const shown = raw.slice(0, BLOCK_PREVIEW_LINES);
|
|
1013
|
+
const styled = block.language ? highlightCode(shown.join("\n"), block.language) : shown;
|
|
1014
|
+
const rows = styled.map(row => truncateToWidth(replaceTabs(row), inner));
|
|
1015
|
+
if (raw.length > shown.length) {
|
|
1016
|
+
rows.push(theme.fg("dim", `… +${raw.length - shown.length} more lines`));
|
|
1017
|
+
}
|
|
1018
|
+
return { rows, lineCount: raw.length };
|
|
1019
|
+
});
|
|
1020
|
+
this.#blockPreviews = { blocks, inner, previews };
|
|
1021
|
+
return previews;
|
|
1022
|
+
}
|
|
999
1023
|
}
|
|
1000
1024
|
|
|
1001
1025
|
/** Raw multi-line text of a user message (string or text blocks). */
|
|
@@ -25,7 +25,7 @@ import { Memo } from "../../native/memo";
|
|
|
25
25
|
import type { DescribeContext, NativeNode, NativeScroll, NativeUiEvent } from "../../native/node";
|
|
26
26
|
import { actionBar, actionButton } from "../../native/overlay";
|
|
27
27
|
import { CLOSE_ACTION, type PickerEvent, picker, pickerAction, pickerEvent } from "../../native/picker";
|
|
28
|
-
import { matchesKey } from "../../keys";
|
|
28
|
+
import { type KeyId, matchesKey } from "../../keys";
|
|
29
29
|
import { parseSgrMouse } from "../../mouse";
|
|
30
30
|
import { SplitPane, type SplitPaneHit } from "../../components/layout/split-pane";
|
|
31
31
|
import { Stack } from "../../components/layout/stack";
|
|
@@ -41,7 +41,7 @@ import {
|
|
|
41
41
|
matchesSelectPageUp,
|
|
42
42
|
} from "../../keybinding-matchers";
|
|
43
43
|
import { expandKeyHint } from "../../render/render-utils";
|
|
44
|
-
import { formatKeyHint, formatKeyHints } from "../../app-keybindings";
|
|
44
|
+
import { formatKeyHint, formatKeyHints, formatTooltipKey } from "../../app-keybindings";
|
|
45
45
|
import { boundKeys, editorKeys, interruptKey } from "../../chrome/keybinding-hints";
|
|
46
46
|
import { bottomBorder, divider, PanelRows, row, topBorder } from "../../chrome/overlay-box";
|
|
47
47
|
import { ExtensionList, type ExtensionListSwitch } from "./extension-list";
|
|
@@ -694,7 +694,7 @@ export class ExtensionDashboard implements Component {
|
|
|
694
694
|
];
|
|
695
695
|
}
|
|
696
696
|
|
|
697
|
-
#expandKey():
|
|
697
|
+
#expandKey(): KeyId {
|
|
698
698
|
return boundKeys("app.tools.expand", ["ctrl+o"])[0] ?? "ctrl+o";
|
|
699
699
|
}
|
|
700
700
|
|
|
@@ -714,7 +714,7 @@ export class ExtensionDashboard implements Component {
|
|
|
714
714
|
node("icon", {
|
|
715
715
|
name: "x",
|
|
716
716
|
role: "zeta.app.ibtn",
|
|
717
|
-
title: `Close ${
|
|
717
|
+
title: `Close ${formatTooltipKey(boundKeys("app.interrupt", ["escape"])[0] ?? "escape")}`,
|
|
718
718
|
aria: "Close",
|
|
719
719
|
actions: { click: "close" },
|
|
720
720
|
}),
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Pure dashboard tree, filtering, and selection state. */
|
|
2
|
-
import {
|
|
2
|
+
import { FuzzyQuery, FuzzyText } from "../../fuzzy";
|
|
3
3
|
import {
|
|
4
4
|
type DashboardState,
|
|
5
5
|
type DisabledReason,
|
|
@@ -119,6 +119,50 @@ export function flattenTree(tree: TreeNode[]): FlatTreeItem[] {
|
|
|
119
119
|
return flat;
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
+
/** Searchable fields an extension's cached {@link FuzzyText} was built from. */
|
|
123
|
+
interface ExtensionSearchMemo {
|
|
124
|
+
name: string;
|
|
125
|
+
displayName: string;
|
|
126
|
+
description: string | undefined;
|
|
127
|
+
trigger: string | undefined;
|
|
128
|
+
providerName: string;
|
|
129
|
+
kind: string;
|
|
130
|
+
text: FuzzyText;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** Prepared search text per extension; rebuilt if any searchable field changes. */
|
|
134
|
+
const extensionSearchCache = new WeakMap<Extension, ExtensionSearchMemo>();
|
|
135
|
+
|
|
136
|
+
function extensionSearchText(ext: Extension): FuzzyText {
|
|
137
|
+
const memo = extensionSearchCache.get(ext);
|
|
138
|
+
if (
|
|
139
|
+
memo !== undefined &&
|
|
140
|
+
memo.name === ext.name &&
|
|
141
|
+
memo.displayName === ext.displayName &&
|
|
142
|
+
memo.description === ext.description &&
|
|
143
|
+
memo.trigger === ext.trigger &&
|
|
144
|
+
memo.providerName === ext.source.providerName &&
|
|
145
|
+
memo.kind === ext.kind
|
|
146
|
+
) {
|
|
147
|
+
return memo.text;
|
|
148
|
+
}
|
|
149
|
+
const text = new FuzzyText(
|
|
150
|
+
[ext.name, ext.displayName, ext.description || "", ext.trigger || "", ext.source.providerName, ext.kind].join(
|
|
151
|
+
" ",
|
|
152
|
+
),
|
|
153
|
+
);
|
|
154
|
+
extensionSearchCache.set(ext, {
|
|
155
|
+
name: ext.name,
|
|
156
|
+
displayName: ext.displayName,
|
|
157
|
+
description: ext.description,
|
|
158
|
+
trigger: ext.trigger,
|
|
159
|
+
providerName: ext.source.providerName,
|
|
160
|
+
kind: ext.kind,
|
|
161
|
+
text,
|
|
162
|
+
});
|
|
163
|
+
return text;
|
|
164
|
+
}
|
|
165
|
+
|
|
122
166
|
/**
|
|
123
167
|
* Apply fuzzy filter to extensions.
|
|
124
168
|
*/
|
|
@@ -127,22 +171,18 @@ export function applyFilter(extensions: Extension[], query: string): Extension[]
|
|
|
127
171
|
return extensions;
|
|
128
172
|
}
|
|
129
173
|
|
|
130
|
-
const tokens = query
|
|
174
|
+
const tokens = query
|
|
175
|
+
.toLowerCase()
|
|
176
|
+
.split(/\s+/)
|
|
177
|
+
.filter(Boolean)
|
|
178
|
+
.map(token => new FuzzyQuery(token));
|
|
131
179
|
if (tokens.length === 0) {
|
|
132
180
|
return extensions;
|
|
133
181
|
}
|
|
134
182
|
|
|
135
183
|
return extensions.filter(ext => {
|
|
136
|
-
const searchable =
|
|
137
|
-
|
|
138
|
-
ext.displayName,
|
|
139
|
-
ext.description || "",
|
|
140
|
-
ext.trigger || "",
|
|
141
|
-
ext.source.providerName,
|
|
142
|
-
ext.kind,
|
|
143
|
-
].join(" ");
|
|
144
|
-
|
|
145
|
-
return tokens.every(token => fuzzyMatch(token, searchable).matches);
|
|
184
|
+
const searchable = extensionSearchText(ext);
|
|
185
|
+
return tokens.every(token => token.match(searchable).matches);
|
|
146
186
|
});
|
|
147
187
|
}
|
|
148
188
|
|