@oh-my-pi/pi-tui 18.4.2 → 18.4.4
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 +56 -0
- package/dist/types/apps/cleanse-board.d.ts +17 -0
- package/dist/types/apps/git/help.d.ts +16 -0
- package/dist/types/apps/git/sidebar.d.ts +18 -0
- package/dist/types/apps/live-visualizer.d.ts +10 -0
- package/dist/types/apps/ps-top.d.ts +15 -0
- package/dist/types/apps/standalone-picker.d.ts +3 -0
- package/dist/types/autocomplete.d.ts +8 -0
- package/dist/types/chat/advisor-message.d.ts +12 -2
- package/dist/types/chat/assistant-message.d.ts +20 -0
- package/dist/types/chat/bash-execution.d.ts +9 -0
- package/dist/types/chat/chat-transcript-builder.d.ts +6 -1
- package/dist/types/chat/collab-prompt-message.d.ts +4 -0
- package/dist/types/chat/compaction-summary-message.d.ts +15 -0
- package/dist/types/chat/eval-execution.d.ts +8 -0
- package/dist/types/chat/execution-shared.d.ts +58 -0
- package/dist/types/chat/late-diagnostics-message.d.ts +15 -1
- package/dist/types/chat/read-tool-group.d.ts +15 -0
- package/dist/types/chat/skill-message.d.ts +9 -0
- package/dist/types/chat/stripped-tool-calls-placeholder.d.ts +3 -0
- package/dist/types/chat/thinking-display.d.ts +2 -0
- package/dist/types/chat/todo-reminder.d.ts +4 -1
- package/dist/types/chat/tool-execution.d.ts +25 -1
- package/dist/types/chat/transcript-actions.d.ts +37 -0
- package/dist/types/chat/transcript-entry.d.ts +3 -1
- package/dist/types/chat/user-message.d.ts +25 -0
- package/dist/types/chrome/collab-qrcode.d.ts +7 -0
- package/dist/types/chrome/context-thresholds.d.ts +11 -0
- package/dist/types/chrome/countdown-timer.d.ts +9 -3
- package/dist/types/chrome/diff.d.ts +12 -0
- package/dist/types/chrome/dynamic-border.d.ts +3 -0
- package/dist/types/chrome/format.d.ts +6 -0
- package/dist/types/chrome/message-divider.d.ts +13 -0
- package/dist/types/chrome/message-frame.d.ts +10 -0
- package/dist/types/chrome/message-notice.d.ts +23 -0
- package/dist/types/chrome/overlay-box.d.ts +11 -2
- package/dist/types/chrome/qrcode.d.ts +7 -0
- package/dist/types/chrome/segment-track.d.ts +7 -0
- package/dist/types/chrome/selector-helpers.d.ts +0 -6
- package/dist/types/chrome/status-notice.d.ts +14 -0
- package/dist/types/chrome/streaming-panel.d.ts +3 -0
- package/dist/types/chrome/tool-activity.d.ts +3 -0
- package/dist/types/chrome/transcript-container.d.ts +13 -0
- package/dist/types/chrome/transcript-status.d.ts +15 -3
- package/dist/types/components/box.d.ts +7 -0
- package/dist/types/components/disclosure.d.ts +9 -0
- package/dist/types/components/editor.d.ts +42 -0
- package/dist/types/components/form.d.ts +14 -0
- package/dist/types/components/image.d.ts +7 -0
- package/dist/types/components/input.d.ts +20 -1
- package/dist/types/components/key-value-list.d.ts +3 -0
- package/dist/types/components/layout/geometry.d.ts +16 -0
- package/dist/types/components/layout/row.d.ts +7 -0
- package/dist/types/components/layout/split-pane.d.ts +8 -0
- package/dist/types/components/layout/stack.d.ts +6 -0
- package/dist/types/components/loader.d.ts +48 -0
- package/dist/types/components/markdown.d.ts +8 -0
- package/dist/types/components/metric.d.ts +7 -0
- package/dist/types/components/progress-bar.d.ts +3 -0
- package/dist/types/components/scroll-view.d.ts +6 -0
- package/dist/types/components/section.d.ts +3 -0
- package/dist/types/components/select-list.d.ts +33 -0
- package/dist/types/components/settings-list.d.ts +68 -0
- package/dist/types/components/spacer.d.ts +4 -0
- package/dist/types/components/tab-bar.d.ts +9 -0
- package/dist/types/components/table.d.ts +7 -0
- package/dist/types/components/text.d.ts +6 -0
- package/dist/types/components/tree-view.d.ts +9 -0
- package/dist/types/components/truncated-text.d.ts +3 -0
- package/dist/types/components/wizard-step.d.ts +6 -0
- package/dist/types/hotkeys-markdown.d.ts +24 -0
- package/dist/types/index.d.ts +5 -0
- package/dist/types/latex-block.d.ts +7 -0
- package/dist/types/loop-watchdog.d.ts +9 -0
- package/dist/types/native/apply.d.ts +39 -0
- package/dist/types/native/backend.d.ts +94 -0
- package/dist/types/native/blobs.d.ts +29 -0
- package/dist/types/native/describe.d.ts +56 -0
- package/dist/types/native/encode.d.ts +50 -0
- package/dist/types/native/icons.d.ts +18 -0
- package/dist/types/native/memo.d.ts +27 -0
- package/dist/types/native/node.d.ts +107 -0
- package/dist/types/native/overlay.d.ts +64 -0
- package/dist/types/native/picker.d.ts +124 -0
- package/dist/types/native/reconcile.d.ts +41 -0
- package/dist/types/native/settle.d.ts +11 -0
- package/dist/types/native/spans.d.ts +21 -0
- package/dist/types/native/state.d.ts +6 -0
- package/dist/types/native/tone.d.ts +24 -0
- package/dist/types/overlays/advisor-config.d.ts +20 -0
- package/dist/types/overlays/agent-hub-renderer.d.ts +27 -11
- package/dist/types/overlays/agent-hub.d.ts +10 -0
- package/dist/types/overlays/agent-transcript-viewer.d.ts +7 -0
- package/dist/types/overlays/agents-hub.d.ts +8 -0
- package/dist/types/overlays/annotation-overlay.d.ts +3 -0
- package/dist/types/overlays/ask-dialog.d.ts +23 -0
- package/dist/types/overlays/bordered-loader.d.ts +3 -0
- package/dist/types/overlays/btw-history-panel.d.ts +9 -0
- package/dist/types/overlays/btw-panel.d.ts +3 -0
- package/dist/types/overlays/cleanse-panel.d.ts +5 -2
- package/dist/types/overlays/composer-shape-preview.d.ts +7 -1
- package/dist/types/overlays/copy-selector.d.ts +38 -0
- package/dist/types/overlays/error-banner.d.ts +9 -0
- package/dist/types/overlays/extensions/extension-list.d.ts +1 -0
- package/dist/types/overlays/history-search.d.ts +18 -0
- package/dist/types/overlays/hook-editor.d.ts +28 -1
- package/dist/types/overlays/hook-input.d.ts +3 -0
- package/dist/types/overlays/hook-selector.d.ts +10 -0
- package/dist/types/overlays/hub-frame.d.ts +18 -0
- package/dist/types/overlays/jobs-panel.d.ts +24 -0
- package/dist/types/overlays/login-dialog.d.ts +10 -0
- package/dist/types/overlays/logout-account-selector.d.ts +4 -0
- package/dist/types/overlays/mcp-add-wizard.d.ts +4 -0
- package/dist/types/overlays/model-browser.d.ts +69 -0
- package/dist/types/overlays/model-hub.d.ts +7 -0
- package/dist/types/overlays/model-picker.d.ts +9 -1
- package/dist/types/overlays/move-overlay.d.ts +3 -0
- package/dist/types/overlays/oauth-selector.d.ts +4 -0
- package/dist/types/overlays/omfg-panel.d.ts +3 -0
- package/dist/types/overlays/pause-screen.d.ts +12 -0
- package/dist/types/overlays/plan-review-overlay.d.ts +5 -0
- package/dist/types/overlays/plan-save-overlay.d.ts +10 -0
- package/dist/types/overlays/plugin-selector.d.ts +4 -0
- package/dist/types/overlays/plugin-settings.d.ts +30 -0
- package/dist/types/overlays/queue-mode-selector.d.ts +4 -0
- package/dist/types/overlays/reset-usage-selector.d.ts +8 -0
- package/dist/types/overlays/rewind-selector.d.ts +5 -0
- package/dist/types/overlays/session-info-overlay.d.ts +19 -1
- package/dist/types/overlays/session-selector.d.ts +64 -1
- package/dist/types/overlays/settings-defs.d.ts +14 -0
- package/dist/types/overlays/settings-selector.d.ts +21 -0
- package/dist/types/overlays/show-images-selector.d.ts +4 -0
- package/dist/types/overlays/snapcompact-shape-preview.d.ts +2 -0
- package/dist/types/overlays/stats-notice.d.ts +8 -0
- package/dist/types/overlays/theme-selector.d.ts +11 -0
- package/dist/types/overlays/thinking-selector.d.ts +4 -0
- package/dist/types/overlays/tree-selector.d.ts +60 -2
- package/dist/types/overlays/usage-dashboard.d.ts +26 -3
- package/dist/types/overlays/usage-row.d.ts +36 -2
- package/dist/types/prompt/attachment-chips.d.ts +8 -0
- package/dist/types/prompt/composer-attachments.d.ts +15 -3
- package/dist/types/prompt/composer.d.ts +18 -1
- package/dist/types/prompt/custom-editor.d.ts +48 -2
- package/dist/types/prompt/editor-top-gap.d.ts +2 -0
- package/dist/types/prompt/macos-spelling.d.ts +6 -0
- package/dist/types/prompt/magic-keywords.d.ts +9 -0
- package/dist/types/prompt/queued-messages.d.ts +23 -0
- package/dist/types/prompt/skill-tokens.d.ts +4 -2
- package/dist/types/prompt/welcome.d.ts +16 -0
- package/dist/types/render/code-cell.d.ts +10 -0
- package/dist/types/render/file-list.d.ts +8 -0
- package/dist/types/render/hyperlink.d.ts +14 -0
- package/dist/types/render/output-block.d.ts +43 -0
- package/dist/types/render/output-pane.d.ts +17 -0
- package/dist/types/render/tool-card.d.ts +8 -0
- package/dist/types/render/tree-list.d.ts +8 -0
- package/dist/types/render/width-aware-text.d.ts +7 -0
- package/dist/types/setup/scenes/outro.d.ts +7 -0
- package/dist/types/setup/scenes/sign-in.d.ts +16 -9
- package/dist/types/setup/scenes/splash.d.ts +8 -0
- package/dist/types/setup/scenes/types.d.ts +6 -27
- package/dist/types/setup/wizard-overlay.d.ts +9 -0
- package/dist/types/status-line/component.d.ts +49 -5
- package/dist/types/status-line/context-usage.d.ts +20 -0
- package/dist/types/status-line/footer.d.ts +8 -0
- package/dist/types/status-line/metrics.d.ts +1 -2
- package/dist/types/status-line/segments.d.ts +10 -1
- package/dist/types/status-line/types.d.ts +40 -1
- package/dist/types/stdin-buffer.d.ts +10 -7
- package/dist/types/terminal-capabilities.d.ts +1 -1
- package/dist/types/terminal.d.ts +36 -0
- package/dist/types/theme/defaults/index.d.ts +100 -0
- package/dist/types/theme/schema.d.ts +2 -0
- package/dist/types/theme/shimmer.d.ts +8 -0
- package/dist/types/theme/theme-class.d.ts +2 -0
- package/dist/types/theme/theme.d.ts +33 -1
- package/dist/types/tools/ask.d.ts +3 -0
- package/dist/types/tools/ast-edit.d.ts +10 -1
- package/dist/types/tools/ast-grep.d.ts +10 -1
- package/dist/types/tools/autoresearch.d.ts +21 -4
- package/dist/types/tools/bash.d.ts +30 -1
- package/dist/types/tools/cfg-render.d.ts +5 -1
- package/dist/types/tools/debug.d.ts +3 -1
- package/dist/types/tools/default-renderer.d.ts +6 -1
- package/dist/types/tools/edit.d.ts +14 -1
- package/dist/types/tools/eval.d.ts +12 -1
- package/dist/types/tools/fetch.d.ts +16 -1
- package/dist/types/tools/find.d.ts +15 -1
- package/dist/types/tools/github.d.ts +3 -0
- package/dist/types/tools/glob.d.ts +14 -1
- package/dist/types/tools/goal.d.ts +3 -1
- package/dist/types/tools/grep.d.ts +14 -1
- package/dist/types/tools/grouped-file-output.d.ts +58 -0
- package/dist/types/tools/json-tree.d.ts +20 -0
- package/dist/types/tools/lsp.d.ts +3 -10
- package/dist/types/tools/mcp.d.ts +16 -1
- package/dist/types/tools/memory.d.ts +26 -0
- package/dist/types/tools/native-view.d.ts +90 -0
- package/dist/types/tools/proc-render.d.ts +7 -1
- package/dist/types/tools/read.d.ts +23 -1
- package/dist/types/tools/renderer.d.ts +57 -0
- package/dist/types/tools/report-tool-issue.d.ts +3 -0
- package/dist/types/tools/resolve.d.ts +14 -1
- package/dist/types/tools/task.d.ts +3 -0
- package/dist/types/tools/think.d.ts +3 -0
- package/dist/types/tools/todo.d.ts +12 -0
- package/dist/types/tools/vibe.d.ts +3 -1
- package/dist/types/tools/wait.d.ts +3 -0
- package/dist/types/tools/web-search-types.d.ts +90 -0
- package/dist/types/tools/web-search.d.ts +9 -168
- package/dist/types/tools/write.d.ts +14 -1
- package/dist/types/tools/xdev.d.ts +13 -0
- package/dist/types/tui.d.ts +49 -0
- package/package.json +9 -9
- package/src/apps/autoresearch-dashboard.ts +362 -4
- package/src/apps/cleanse-board.ts +219 -1
- package/src/apps/cleanse-picker.ts +9 -1
- package/src/apps/debug/log-viewer.ts +15 -15
- package/src/apps/debug/protocol-probe.ts +2 -20
- package/src/apps/git/avatar.ts +2 -22
- package/src/apps/git/git-tui.ts +406 -59
- package/src/apps/git/help.ts +130 -0
- package/src/apps/git/sidebar.ts +491 -0
- package/src/apps/live-visualizer.ts +65 -0
- package/src/apps/ps-top.ts +448 -17
- package/src/apps/session-picker.ts +1 -0
- package/src/apps/setup-model-picker.ts +5 -1
- package/src/apps/standalone-picker.ts +55 -1
- package/src/autocomplete.ts +44 -1
- package/src/chat/advisor-message.ts +72 -3
- package/src/chat/assistant-message.ts +538 -51
- package/src/chat/background-tan-message.ts +4 -4
- package/src/chat/bash-execution.ts +81 -0
- package/src/chat/cache-invalidation-marker.ts +8 -0
- package/src/chat/chat-transcript-builder.ts +37 -6
- package/src/chat/collab-prompt-message.ts +15 -3
- package/src/chat/compaction-summary-message.ts +74 -1
- package/src/chat/custom-message.ts +3 -1
- package/src/chat/eval-execution.ts +57 -0
- package/src/chat/execution-shared.ts +153 -1
- package/src/chat/hook-message.ts +1 -0
- package/src/chat/late-diagnostics-message.ts +85 -2
- package/src/chat/read-tool-group.ts +341 -1
- package/src/chat/served-model-marker.ts +8 -0
- package/src/chat/skill-message.ts +63 -0
- package/src/chat/stripped-tool-calls-placeholder.ts +18 -10
- package/src/chat/thinking-display.ts +73 -11
- package/src/chat/todo-reminder.ts +34 -4
- package/src/chat/tool-execution.ts +385 -10
- package/src/chat/transcript-actions.ts +38 -0
- package/src/chat/transcript-browser.ts +2 -9
- package/src/chat/transcript-entry.ts +10 -1
- package/src/chat/transcript-render-helpers.ts +16 -15
- package/src/chat/ttsr-notification.ts +38 -1
- package/src/chat/user-message.ts +167 -4
- package/src/chrome/collab-qrcode.ts +27 -2
- package/src/chrome/context-thresholds.ts +34 -0
- package/src/chrome/countdown-timer.ts +24 -1
- package/src/chrome/diff.ts +60 -1
- package/src/chrome/dynamic-border.ts +7 -0
- package/src/chrome/format.ts +12 -0
- package/src/chrome/message-divider.ts +33 -1
- package/src/chrome/message-frame.ts +53 -10
- package/src/chrome/message-notice.ts +71 -0
- package/src/chrome/overlay-box.ts +45 -2
- package/src/chrome/qrcode.ts +28 -0
- package/src/chrome/segment-track.ts +26 -0
- package/src/chrome/selector-helpers.ts +1 -17
- package/src/chrome/status-notice.ts +46 -0
- package/src/chrome/streaming-panel.ts +25 -1
- package/src/chrome/tool-activity.ts +20 -0
- package/src/chrome/transcript-container.ts +139 -17
- package/src/chrome/transcript-status.ts +40 -11
- package/src/components/box.ts +51 -0
- package/src/components/disclosure.ts +33 -0
- package/src/components/editor.ts +239 -36
- package/src/components/form.ts +93 -9
- package/src/components/image.ts +30 -0
- package/src/components/input.ts +77 -26
- package/src/components/key-value-list.ts +18 -0
- package/src/components/layout/geometry.ts +44 -0
- package/src/components/layout/row.ts +30 -0
- package/src/components/layout/split-pane.ts +65 -0
- package/src/components/layout/stack.ts +27 -0
- package/src/components/loader.ts +183 -1
- package/src/components/markdown.ts +131 -56
- package/src/components/metric.ts +39 -0
- package/src/components/progress-bar.ts +28 -0
- package/src/components/scroll-view.ts +23 -0
- package/src/components/section.ts +16 -0
- package/src/components/select-list.ts +174 -25
- package/src/components/settings-list.ts +264 -7
- package/src/components/spacer.ts +17 -0
- package/src/components/tab-bar.ts +42 -0
- package/src/components/table.ts +34 -0
- package/src/components/text.ts +23 -0
- package/src/components/tree-view.ts +65 -0
- package/src/components/truncated-text.ts +14 -0
- package/src/components/wizard-step.ts +18 -9
- package/src/debug-server.ts +14 -7
- package/src/hotkeys-markdown.ts +251 -69
- package/src/index.ts +6 -0
- package/src/latex-block.ts +11 -0
- package/src/loop-watchdog.ts +38 -10
- package/src/native/apply.ts +275 -0
- package/src/native/backend.ts +682 -0
- package/src/native/blobs.ts +70 -0
- package/src/native/describe.ts +124 -0
- package/src/native/encode.ts +233 -0
- package/src/native/icons.ts +184 -0
- package/src/native/memo.ts +64 -0
- package/src/native/node.ts +112 -0
- package/src/native/overlay.ts +131 -0
- package/src/native/picker.ts +333 -0
- package/src/native/reconcile.ts +1063 -0
- package/src/native/settle.ts +23 -0
- package/src/native/spans.ts +308 -0
- package/src/native/state.ts +28 -0
- package/src/native/tone.ts +79 -0
- package/src/overlays/advisor-config.ts +420 -0
- package/src/overlays/agent-hub-renderer.ts +155 -18
- package/src/overlays/agent-hub.ts +1037 -44
- package/src/overlays/agent-transcript-viewer.ts +129 -0
- package/src/overlays/agents-hub.ts +688 -53
- package/src/overlays/annotation-overlay.ts +391 -52
- package/src/overlays/ask-dialog.ts +435 -24
- package/src/overlays/bordered-loader.ts +17 -0
- package/src/overlays/btw-history-panel.ts +182 -0
- package/src/overlays/btw-panel.ts +68 -2
- package/src/overlays/cleanse-panel.ts +105 -15
- package/src/overlays/codex-reset-fireworks.ts +65 -5
- package/src/overlays/composer-shape-preview.ts +26 -1
- package/src/overlays/copy-selector.ts +545 -23
- package/src/overlays/error-banner.ts +44 -0
- package/src/overlays/extensions/extension-list.ts +23 -17
- package/src/overlays/history-search.ts +175 -33
- package/src/overlays/hook-editor.ts +113 -6
- package/src/overlays/hook-input.ts +22 -1
- package/src/overlays/hook-selector.ts +311 -22
- package/src/overlays/hub-frame.ts +80 -0
- package/src/overlays/jobs-panel.ts +112 -0
- package/src/overlays/login-dialog.ts +183 -1
- package/src/overlays/logout-account-selector.ts +102 -4
- package/src/overlays/mcp-add-wizard.ts +232 -53
- package/src/overlays/model-browser.ts +681 -22
- package/src/overlays/model-hub.ts +1402 -78
- package/src/overlays/model-picker.ts +184 -7
- package/src/overlays/move-overlay.ts +45 -0
- package/src/overlays/oauth-selector.ts +227 -23
- package/src/overlays/omfg-panel.ts +86 -2
- package/src/overlays/pause-screen.ts +55 -4
- package/src/overlays/plan-review-overlay.ts +352 -0
- package/src/overlays/plan-save-overlay.ts +45 -0
- package/src/overlays/plugin-selector.ts +21 -1
- package/src/overlays/plugin-settings.ts +195 -5
- package/src/overlays/queue-mode-selector.ts +19 -1
- package/src/overlays/reset-usage-selector.ts +110 -40
- package/src/overlays/rewind-selector.ts +458 -44
- package/src/overlays/session-account-selector.ts +1 -1
- package/src/overlays/session-info-overlay.ts +137 -1
- package/src/overlays/session-selector.ts +807 -46
- package/src/overlays/settings-defs.ts +41 -0
- package/src/overlays/settings-selector.ts +585 -33
- package/src/overlays/show-images-selector.ts +18 -1
- package/src/overlays/snapcompact-shape-preview.ts +48 -3
- package/src/overlays/stats-notice.ts +31 -0
- package/src/overlays/theme-selector.ts +31 -1
- package/src/overlays/thinking-selector.ts +22 -1
- package/src/overlays/tree-selector.ts +570 -110
- package/src/overlays/usage-dashboard.ts +587 -12
- package/src/overlays/usage-row.ts +161 -25
- package/src/prompt/attachment-chips.ts +60 -0
- package/src/prompt/composer-attachments.ts +31 -9
- package/src/prompt/composer.ts +55 -2
- package/src/prompt/custom-editor.ts +302 -4
- package/src/prompt/editor-top-gap.ts +8 -0
- package/src/prompt/macos-spelling.ts +24 -9
- package/src/prompt/magic-keywords.ts +22 -1
- package/src/prompt/model-mention-autocomplete.ts +1 -0
- package/src/prompt/queued-messages.ts +101 -0
- package/src/prompt/skill-tokens.ts +5 -3
- package/src/prompt/welcome.ts +213 -6
- package/src/render/code-cell.ts +103 -2
- package/src/render/file-list.ts +29 -0
- package/src/render/hyperlink.ts +32 -0
- package/src/render/output-block.ts +110 -0
- package/src/render/output-pane.ts +37 -0
- package/src/render/tool-card.ts +137 -0
- package/src/render/tree-list.ts +39 -0
- package/src/render/width-aware-text.ts +20 -0
- package/src/setup/scenes/composer.ts +29 -0
- package/src/setup/scenes/glyph.ts +15 -0
- package/src/setup/scenes/model.ts +36 -8
- package/src/setup/scenes/outro.ts +28 -1
- package/src/setup/scenes/sign-in.ts +138 -24
- package/src/setup/scenes/splash.ts +28 -1
- package/src/setup/scenes/theme.ts +88 -3
- package/src/setup/scenes/types.ts +7 -28
- package/src/setup/startup-splash.ts +46 -9
- package/src/setup/wizard-overlay.ts +140 -23
- package/src/setup/wizard.ts +1 -1
- package/src/status-line/component.ts +414 -18
- package/src/status-line/context-usage.ts +279 -50
- package/src/status-line/footer.ts +94 -1
- package/src/status-line/metrics.ts +3 -6
- package/src/status-line/segments.ts +463 -97
- package/src/status-line/separators.ts +3 -16
- package/src/status-line/types.ts +43 -1
- package/src/stdin-buffer.ts +27 -21
- package/src/terminal-capabilities.ts +7 -0
- package/src/terminal.ts +153 -11
- package/src/theme/defaults/dark-neon-noir.json +100 -0
- package/src/theme/defaults/index.ts +2 -0
- package/src/theme/defaults/light-prism.json +5 -5
- package/src/theme/defaults/onyx.json +62 -62
- package/src/theme/mermaid-cache.ts +10 -1
- package/src/theme/schema.ts +15 -0
- package/src/theme/shimmer.ts +33 -0
- package/src/theme/theme-class.ts +24 -19
- package/src/theme/theme.ts +111 -9
- package/src/theme/tui-adapters.ts +14 -1
- package/src/tools/ask.ts +157 -1
- package/src/tools/ast-edit.ts +174 -1
- package/src/tools/ast-grep.ts +82 -2
- package/src/tools/autoresearch.ts +149 -1
- package/src/tools/bash.ts +233 -62
- package/src/tools/cfg-render.ts +66 -1
- package/src/tools/debug.ts +78 -1
- package/src/tools/default-renderer.ts +35 -1
- package/src/tools/edit.ts +357 -31
- package/src/tools/eval.ts +257 -1
- package/src/tools/fetch.ts +102 -3
- package/src/tools/find.ts +127 -1
- package/src/tools/github.ts +119 -10
- package/src/tools/glob.ts +116 -1
- package/src/tools/goal.ts +56 -1
- package/src/tools/grep.ts +83 -2
- package/src/tools/grouped-file-output.ts +205 -0
- package/src/tools/json-tree.ts +116 -1
- package/src/tools/lsp.ts +318 -1
- package/src/tools/mcp.ts +80 -1
- package/src/tools/memory.ts +93 -11
- package/src/tools/native-view.ts +292 -0
- package/src/tools/proc-render.ts +221 -1
- package/src/tools/read.ts +199 -4
- package/src/tools/renderer.ts +61 -0
- package/src/tools/report-tool-issue.ts +7 -1
- package/src/tools/resolve.ts +63 -1
- package/src/tools/streaming-output.ts +79 -16
- package/src/tools/task.ts +406 -4
- package/src/tools/think.ts +19 -1
- package/src/tools/todo.ts +89 -0
- package/src/tools/vibe.ts +230 -2
- package/src/tools/wait.ts +181 -1
- package/src/tools/web-search-types.ts +99 -0
- package/src/tools/web-search.ts +111 -170
- package/src/tools/write.ts +144 -5
- package/src/tools/xdev.ts +64 -1
- package/src/tui.ts +295 -4
- package/dist/types/setup/scenes/providers.d.ts +0 -3
- package/dist/types/setup/scenes/web-search.d.ts +0 -22
- package/src/setup/scenes/providers.ts +0 -110
- package/src/setup/scenes/web-search.ts +0 -165
|
@@ -3,6 +3,10 @@ import { WidthAwareText } from "../render/index";
|
|
|
3
3
|
import { theme } from "../theme/theme";
|
|
4
4
|
import { DynamicBorder } from "../chrome/dynamic-border";
|
|
5
5
|
import { formatErrorBlock } from "../chrome/error-block";
|
|
6
|
+
import type { NativeNode, NativeUiEvent } from "../native/node";
|
|
7
|
+
import { node, span, text } from "../native/describe";
|
|
8
|
+
import { actionButton } from "../native/overlay";
|
|
9
|
+
import { plainText } from "../native/spans";
|
|
6
10
|
|
|
7
11
|
/** Max wrapped rows of the error message shown in the pinned banner. */
|
|
8
12
|
const MAX_BANNER_ROWS = 4;
|
|
@@ -17,8 +21,14 @@ const MAX_BANNER_ROWS = 4;
|
|
|
17
21
|
* overflow row reveals the full body inline in the transcript.
|
|
18
22
|
*/
|
|
19
23
|
export class ErrorBannerComponent extends Container {
|
|
24
|
+
readonly #message: string;
|
|
25
|
+
/** Natively the strip shows the headline; Details unfolds the whole message in place. */
|
|
26
|
+
#expanded = false;
|
|
27
|
+
#native: NativeNode | undefined;
|
|
28
|
+
|
|
20
29
|
constructor(message: string) {
|
|
21
30
|
super();
|
|
31
|
+
this.#message = plainText(message).trim();
|
|
22
32
|
this.addChild(new Spacer(1));
|
|
23
33
|
this.addChild(new DynamicBorder(str => theme.fg("error", str)));
|
|
24
34
|
this.addChild(
|
|
@@ -36,4 +46,38 @@ export class ErrorBannerComponent extends Container {
|
|
|
36
46
|
this.addChild(new Text(theme.fg("dim", "Dismissed when you send your next message."), 1, 0));
|
|
37
47
|
this.addChild(new DynamicBorder(str => theme.fg("error", str)));
|
|
38
48
|
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* An error-tinted strip pinned above the composer: the message's first line
|
|
52
|
+
* (the transcript's error frame keeps the full message and its actions)
|
|
53
|
+
* and a Details button that unfolds the rest here.
|
|
54
|
+
*/
|
|
55
|
+
override describe(): NativeNode {
|
|
56
|
+
if (this.#native) return this.#native;
|
|
57
|
+
const headline =
|
|
58
|
+
this.#message
|
|
59
|
+
.split("\n")
|
|
60
|
+
.find(line => line.trim() !== "")
|
|
61
|
+
?.trim() || "Error";
|
|
62
|
+
const multiline = this.#message !== headline;
|
|
63
|
+
const message = this.#expanded
|
|
64
|
+
? text([span(this.#message, "error")], { wrap: "word", grow: 1 })
|
|
65
|
+
: text([span(headline, "error")], { truncate: "end", lines: 1, grow: 1, title: this.#message });
|
|
66
|
+
const children: NativeNode[] = [message];
|
|
67
|
+
if (multiline || headline.length > 80) {
|
|
68
|
+
children.push(actionButton(this.#expanded ? "Less" : "Details", "details"));
|
|
69
|
+
}
|
|
70
|
+
this.#native = node(
|
|
71
|
+
"row",
|
|
72
|
+
{ role: "omp.errorBanner", tone: "error", gap: "sm", align: this.#expanded ? "start" : "center" },
|
|
73
|
+
children,
|
|
74
|
+
);
|
|
75
|
+
return this.#native;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
handleNativeEvent(event: NativeUiEvent): void {
|
|
79
|
+
if (event.type !== "action" || event.act !== "details") return;
|
|
80
|
+
this.#expanded = !this.#expanded;
|
|
81
|
+
this.#native = undefined;
|
|
82
|
+
}
|
|
39
83
|
}
|
|
@@ -10,7 +10,8 @@ import { matchesKey } from "../../keys";
|
|
|
10
10
|
import { padding, truncateToWidth, visibleWidth } from "../../utils";
|
|
11
11
|
import { theme } from "../../theme";
|
|
12
12
|
import { matchesSelectDown, matchesSelectUp } from "../../keybinding-matchers";
|
|
13
|
-
import { contentRowWidth, renderScrollableList
|
|
13
|
+
import { contentRowWidth, renderScrollableList } from "../../chrome/selector-helpers";
|
|
14
|
+
import { Input } from "../../components/input";
|
|
14
15
|
import { MenuSelection } from "../../components/menu-selection";
|
|
15
16
|
import { scrollOffsetForRow, viewportRange } from "../../components/scroll-viewport";
|
|
16
17
|
import { sanitizeDisplayLine } from "./display-text";
|
|
@@ -102,6 +103,8 @@ export class ExtensionList implements Component {
|
|
|
102
103
|
|
|
103
104
|
#extensions: Extension[];
|
|
104
105
|
readonly #callbacks: ExtensionListCallbacks;
|
|
106
|
+
/** Search field; its value is pushed into the menu query whenever it changes. */
|
|
107
|
+
readonly #search = new Input();
|
|
105
108
|
|
|
106
109
|
constructor(extensions: Extension[], callbacks: ExtensionListCallbacks = {}, maxVisible?: number) {
|
|
107
110
|
this.#extensions = extensions;
|
|
@@ -110,6 +113,7 @@ export class ExtensionList implements Component {
|
|
|
110
113
|
this.#mcpSource = callbacks.mcpSource;
|
|
111
114
|
this.#toolSource = callbacks.toolSource;
|
|
112
115
|
this.#maxVisible = maxVisible ?? DEFAULT_MAX_VISIBLE;
|
|
116
|
+
this.#search.prompt = "";
|
|
113
117
|
this.#menu = new MenuSelection<ListItem>(this.#buildListItems(""), {
|
|
114
118
|
getKey: getListItemKey,
|
|
115
119
|
getSearchText: getListItemSearchText,
|
|
@@ -169,7 +173,9 @@ export class ExtensionList implements Component {
|
|
|
169
173
|
return item?.type === "kind-header" ? item.kind : null;
|
|
170
174
|
}
|
|
171
175
|
|
|
176
|
+
/** Replace the search text (caret to end) and refilter. */
|
|
172
177
|
setSearchQuery(query: string): void {
|
|
178
|
+
if (this.#search.getValue() !== query) this.#search.setValue(query);
|
|
173
179
|
this.#menu.setQuery(query, false);
|
|
174
180
|
this.#scrollOffset = 0;
|
|
175
181
|
this.#notifySelectionChange();
|
|
@@ -188,10 +194,14 @@ export class ExtensionList implements Component {
|
|
|
188
194
|
|
|
189
195
|
// Search bar
|
|
190
196
|
const searchPrefix = theme.fg("muted", "Search: ");
|
|
191
|
-
const query = this.#
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
197
|
+
const query = this.#search.getValue();
|
|
198
|
+
if (this.#focused) {
|
|
199
|
+
const fieldWidth = Math.max(1, width - visibleWidth(searchPrefix));
|
|
200
|
+
const [field = ""] = this.#search.render(Math.min(fieldWidth, visibleWidth(query) + 1));
|
|
201
|
+
lines.push(searchPrefix + field);
|
|
202
|
+
} else {
|
|
203
|
+
lines.push(searchPrefix + (query || theme.fg("dim", "type to filter")));
|
|
204
|
+
}
|
|
195
205
|
lines.push("");
|
|
196
206
|
|
|
197
207
|
const items = this.#menu.visibleItems;
|
|
@@ -631,18 +641,14 @@ export class ExtensionList implements Component {
|
|
|
631
641
|
return;
|
|
632
642
|
}
|
|
633
643
|
|
|
634
|
-
//
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
// Printable characters -> search
|
|
643
|
-
const char = searchableChar(data);
|
|
644
|
-
if (char !== null) {
|
|
645
|
-
this.setSearchQuery(this.#menu.query + char);
|
|
644
|
+
// Everything else edits the search field; refilter only when the text changed.
|
|
645
|
+
const before = this.#search.getValue();
|
|
646
|
+
this.#search.handleInput(data);
|
|
647
|
+
const after = this.#search.getValue();
|
|
648
|
+
if (after !== before) {
|
|
649
|
+
this.#menu.setQuery(after, false);
|
|
650
|
+
this.#scrollOffset = 0;
|
|
651
|
+
this.#notifySelectionChange();
|
|
646
652
|
}
|
|
647
653
|
}
|
|
648
654
|
|
|
@@ -21,6 +21,8 @@ import {
|
|
|
21
21
|
export interface HistorySearchEntry {
|
|
22
22
|
prompt: string;
|
|
23
23
|
created_at: number;
|
|
24
|
+
/** Project folder the prompt was typed in. */
|
|
25
|
+
cwd?: string;
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
/** Searchable prompt history supplied by the host. */
|
|
@@ -28,11 +30,37 @@ export interface HistorySource {
|
|
|
28
30
|
search(query: string, limit: number): HistorySearchEntry[];
|
|
29
31
|
getRecent(limit: number): HistorySearchEntry[];
|
|
30
32
|
}
|
|
31
|
-
import { editorKeys, keyHint, rawKeyHint } from "../chrome/keybinding-hints";
|
|
33
|
+
import { boundKeys, editorKeys, keyHint, rawKeyHint } from "../chrome/keybinding-hints";
|
|
32
34
|
import { OverlayPanel } from "../chrome/overlay-box";
|
|
33
35
|
import { contentRowWidth, renderScrollableList } from "../chrome/selector-helpers";
|
|
34
36
|
import { MenuSelection } from "../components/menu-selection";
|
|
35
37
|
import { centeredViewportRange } from "../components/scroll-viewport";
|
|
38
|
+
import type { KeyName } from "../key-hint-format";
|
|
39
|
+
import type { TspPickerItem } from "@oh-my-pi/pi-wire";
|
|
40
|
+
import { col, keyed, node, span } from "../native/describe";
|
|
41
|
+
import type { DescribeContext, NativeNode, NativeUiEvent } from "../native/node";
|
|
42
|
+
import { actionHint, hintsRow, overlayCard } from "../native/overlay";
|
|
43
|
+
import { picker, pickerAction, pickerAge, pickerDate, pickerEvent, pickerHits, pickerQuery } from "../native/picker";
|
|
44
|
+
import { shortenPath } from "../render/render-utils";
|
|
45
|
+
|
|
46
|
+
const ENTER_KEYS: readonly KeyName[] = ["enter"];
|
|
47
|
+
|
|
48
|
+
/** Native item key of a history entry: key-path safe (prompts may hold `/` and newlines), stable across queries. */
|
|
49
|
+
function nativeEntryKey(entry: HistorySearchEntry): string {
|
|
50
|
+
return `${entry.created_at}-${Bun.hash(entry.prompt).toString(36)}`;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
interface HistoryNativeMemo {
|
|
54
|
+
picker: boolean;
|
|
55
|
+
items: readonly HistorySearchEntry[];
|
|
56
|
+
selected: HistorySearchEntry | undefined;
|
|
57
|
+
query: string;
|
|
58
|
+
cursor: number;
|
|
59
|
+
node: NativeNode;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Key of the `picker` child a dock-mounted history search describes (hoisted into `layer`). */
|
|
63
|
+
const PICKER_KEY = "picker";
|
|
36
64
|
|
|
37
65
|
/** Visible result rows; also the jump distance for PageUp/PageDown. */
|
|
38
66
|
const MAX_VISIBLE = 10;
|
|
@@ -47,46 +75,31 @@ function queryTokens(query: string): string[] {
|
|
|
47
75
|
|
|
48
76
|
/** Wrap every case-insensitive occurrence of any token in `text` with the accent color. */
|
|
49
77
|
function highlightTokens(text: string, tokens: string[]): string {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
const lower = text.toLowerCase();
|
|
53
|
-
const ranges: Array<[number, number]> = [];
|
|
54
|
-
for (const tok of tokens) {
|
|
55
|
-
let from = lower.indexOf(tok);
|
|
56
|
-
while (from !== -1) {
|
|
57
|
-
ranges.push([from, from + tok.length]);
|
|
58
|
-
from = lower.indexOf(tok, from + tok.length);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
78
|
+
const ranges = pickerHits(text, tokens);
|
|
61
79
|
if (ranges.length === 0) return text;
|
|
62
|
-
|
|
63
|
-
ranges.sort((a, b) => a[0] - b[0]);
|
|
64
80
|
let out = "";
|
|
65
81
|
let pos = 0;
|
|
66
82
|
for (const [start, end] of ranges) {
|
|
67
|
-
if (
|
|
68
|
-
|
|
69
|
-
if (from > pos) out += text.slice(pos, from);
|
|
70
|
-
out += theme.fg("accent", text.slice(from, end));
|
|
83
|
+
if (start > pos) out += text.slice(pos, start);
|
|
84
|
+
out += theme.fg("accent", text.slice(start, end));
|
|
71
85
|
pos = end;
|
|
72
86
|
}
|
|
73
87
|
if (pos < text.length) out += text.slice(pos);
|
|
74
88
|
return out;
|
|
75
89
|
}
|
|
76
90
|
|
|
77
|
-
/**
|
|
78
|
-
function
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
return `${Math.floor(days / 365)}y`;
|
|
91
|
+
/** A past prompt as a picker row: its first line (with query hits), age and project folder. */
|
|
92
|
+
function historyPickerItem(entry: HistorySearchEntry, tokens: readonly string[]): TspPickerItem {
|
|
93
|
+
const label = entry.prompt.trim().split("\n", 1)[0]!.replace(/\s+/g, " ").trim();
|
|
94
|
+
const hits = pickerHits(label, tokens);
|
|
95
|
+
return {
|
|
96
|
+
id: nativeEntryKey(entry),
|
|
97
|
+
label,
|
|
98
|
+
...(entry.cwd ? { detail: shortenPath(entry.cwd) } : {}),
|
|
99
|
+
facts: { when: pickerAge(entry.created_at * 1000) },
|
|
100
|
+
...(hits.length > 0 ? { hits } : {}),
|
|
101
|
+
title: pickerDate(entry.created_at * 1000),
|
|
102
|
+
};
|
|
90
103
|
}
|
|
91
104
|
|
|
92
105
|
class HistoryResultsList implements Component {
|
|
@@ -133,7 +146,7 @@ class HistoryResultsList implements Component {
|
|
|
133
146
|
if (!entry) continue;
|
|
134
147
|
const isSelected = i === this.#menu.selectedIndex;
|
|
135
148
|
|
|
136
|
-
const timeStr =
|
|
149
|
+
const timeStr = pickerAge(entry.created_at * 1000);
|
|
137
150
|
const timeWidth = visibleWidth(timeStr);
|
|
138
151
|
const showTime = rowWidth >= gutterWidth + 12 + timeWidth;
|
|
139
152
|
|
|
@@ -170,9 +183,12 @@ export class HistorySearchComponent extends OverlayPanel {
|
|
|
170
183
|
#onSelect: (prompt: string) => void;
|
|
171
184
|
#onCancel: () => void;
|
|
172
185
|
#resultLimit = 100;
|
|
186
|
+
#nativeHints: NativeNode | undefined;
|
|
187
|
+
#nativeMemo: HistoryNativeMemo | undefined;
|
|
188
|
+
#pickerItems: { items: readonly HistorySearchEntry[]; rows: readonly TspPickerItem[] } | undefined;
|
|
173
189
|
|
|
174
190
|
constructor(historyStorage: HistorySource, onSelect: (prompt: string) => void, onCancel: () => void) {
|
|
175
|
-
super("History");
|
|
191
|
+
super("History", "omp.overlay.history");
|
|
176
192
|
this.#historyStorage = historyStorage;
|
|
177
193
|
this.#onSelect = onSelect;
|
|
178
194
|
this.#onCancel = onCancel;
|
|
@@ -257,6 +273,132 @@ export class HistorySearchComponent extends OverlayPanel {
|
|
|
257
273
|
this.#updateResults();
|
|
258
274
|
}
|
|
259
275
|
|
|
276
|
+
/**
|
|
277
|
+
* With the `picker` kind: a `size:"md"` sheet of past prompts (first line
|
|
278
|
+
* with hits, age, folder) as a keyed child the reconciler hoists into
|
|
279
|
+
* `layer`, since the selector replaces the editor in the dock. Otherwise
|
|
280
|
+
* the query field (the `Input`, which describes itself as `input`), the
|
|
281
|
+
* results as a `list` keyed by entry (filter = query, age as the item
|
|
282
|
+
* value), and the key hints.
|
|
283
|
+
*/
|
|
284
|
+
override describe(cx: DescribeContext): NativeNode {
|
|
285
|
+
const items = this.#menu.visibleItems;
|
|
286
|
+
const selected = this.#menu.selectedItem;
|
|
287
|
+
const usePicker = cx.supports("picker");
|
|
288
|
+
const query = usePicker ? this.#searchInput.getValue() : this.#searchInput.getValue().trim();
|
|
289
|
+
const cursor = this.#searchInput.getCursor();
|
|
290
|
+
const memo = this.#nativeMemo;
|
|
291
|
+
if (
|
|
292
|
+
memo &&
|
|
293
|
+
memo.picker === usePicker &&
|
|
294
|
+
memo.items === items &&
|
|
295
|
+
memo.selected === selected &&
|
|
296
|
+
memo.query === query &&
|
|
297
|
+
memo.cursor === cursor
|
|
298
|
+
) {
|
|
299
|
+
return memo.node;
|
|
300
|
+
}
|
|
301
|
+
if (usePicker) {
|
|
302
|
+
const root = col([keyed(this.#describePicker(items, selected, query), PICKER_KEY)]);
|
|
303
|
+
this.#nativeMemo = { picker: true, items, selected, query, cursor, node: root };
|
|
304
|
+
return root;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const rows = items.map(entry =>
|
|
308
|
+
node(
|
|
309
|
+
"item",
|
|
310
|
+
{
|
|
311
|
+
label: entry.prompt.replace(/\s+/g, " ").trim(),
|
|
312
|
+
value: [span(pickerAge(entry.created_at * 1000), "dim")],
|
|
313
|
+
},
|
|
314
|
+
undefined,
|
|
315
|
+
nativeEntryKey(entry),
|
|
316
|
+
),
|
|
317
|
+
);
|
|
318
|
+
const list = node(
|
|
319
|
+
"list",
|
|
320
|
+
{
|
|
321
|
+
selected: selected ? nativeEntryKey(selected) : null,
|
|
322
|
+
filter: query || undefined,
|
|
323
|
+
empty: query ? "No matching history" : "No history yet",
|
|
324
|
+
max: { lines: MAX_VISIBLE },
|
|
325
|
+
virtual: true,
|
|
326
|
+
},
|
|
327
|
+
rows,
|
|
328
|
+
"list",
|
|
329
|
+
);
|
|
330
|
+
this.#nativeHints ??= hintsRow([
|
|
331
|
+
actionHint(["tui.select.up", "tui.select.down"], "navigate"),
|
|
332
|
+
{ keys: ENTER_KEYS, label: "select" },
|
|
333
|
+
actionHint("tui.select.cancel", "cancel"),
|
|
334
|
+
]);
|
|
335
|
+
const root = overlayCard(this.nativeRole, this.title, [this.#searchInput, list, this.#nativeHints]);
|
|
336
|
+
this.#nativeMemo = { picker: false, items, selected, query, cursor, node: root };
|
|
337
|
+
return root;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
#describePicker(
|
|
341
|
+
items: readonly HistorySearchEntry[],
|
|
342
|
+
selected: HistorySearchEntry | undefined,
|
|
343
|
+
query: string,
|
|
344
|
+
): NativeNode {
|
|
345
|
+
let rows = this.#pickerItems;
|
|
346
|
+
if (rows?.items !== items) {
|
|
347
|
+
const tokens = queryTokens(query.trim());
|
|
348
|
+
rows = { items, rows: items.map(entry => historyPickerItem(entry, tokens)) };
|
|
349
|
+
this.#pickerItems = rows;
|
|
350
|
+
}
|
|
351
|
+
return picker({
|
|
352
|
+
title: this.title,
|
|
353
|
+
icon: "history",
|
|
354
|
+
noun: "prompts",
|
|
355
|
+
size: "md",
|
|
356
|
+
layout: "rows",
|
|
357
|
+
preview: "none",
|
|
358
|
+
...pickerQuery(this.#searchInput),
|
|
359
|
+
placeholder: "Search prompts…",
|
|
360
|
+
columns: [{ id: "when", format: "time" }],
|
|
361
|
+
items: rows.rows,
|
|
362
|
+
selected: selected ? nativeEntryKey(selected) : null,
|
|
363
|
+
empty: "No history yet",
|
|
364
|
+
actions: [
|
|
365
|
+
pickerAction("insert", "Insert", "enter", { primary: true }),
|
|
366
|
+
pickerAction("close", "Close", boundKeys("app.interrupt", ["escape"])[0] ?? "escape", { end: true }),
|
|
367
|
+
],
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Picker: a row click highlights, a second click or `Insert` inserts it
|
|
373
|
+
* (Enter), `Close` cancels (Esc), `Clear search` empties the query.
|
|
374
|
+
* List: picking a result does what highlighting it and pressing Enter does.
|
|
375
|
+
*/
|
|
376
|
+
handleNativeEvent(event: NativeUiEvent): void {
|
|
377
|
+
const ev = pickerEvent(event, PICKER_KEY);
|
|
378
|
+
if (ev) {
|
|
379
|
+
if (ev.kind === "action") {
|
|
380
|
+
if (ev.act === "close") this.#onCancel();
|
|
381
|
+
else if (ev.act === "insert") this.handleInput("\r");
|
|
382
|
+
else if (ev.act === "clear") {
|
|
383
|
+
this.#searchInput.setValue("");
|
|
384
|
+
this.#updateResults();
|
|
385
|
+
}
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
const index = this.#menu.visibleItems.findIndex(entry => nativeEntryKey(entry) === ev.item);
|
|
389
|
+
if (index < 0) return;
|
|
390
|
+
this.#menu.setSelectedIndex(index);
|
|
391
|
+
if (ev.kind === "activate") this.#onSelect(this.#menu.visibleItems[index]!.prompt);
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
if ((event.type !== "select" && event.type !== "activate") || event.key !== "list") return;
|
|
395
|
+
const index = this.#menu.visibleItems.findIndex(entry => nativeEntryKey(entry) === event.item);
|
|
396
|
+
const target = this.#menu.visibleItems[index];
|
|
397
|
+
if (!target) return;
|
|
398
|
+
this.#menu.setSelectedIndex(index);
|
|
399
|
+
this.#onSelect(target.prompt);
|
|
400
|
+
}
|
|
401
|
+
|
|
260
402
|
#updateResults(): void {
|
|
261
403
|
const query = this.#searchInput.getValue().trim();
|
|
262
404
|
const results = query
|
|
@@ -7,6 +7,9 @@
|
|
|
7
7
|
* (Ctrl+Q / Ctrl+Enter) submits, bordered popup
|
|
8
8
|
* - Prompt-style (ask): Enter submits, Shift+Enter inserts newline, legacy ask chrome
|
|
9
9
|
*/
|
|
10
|
+
import type { ImageContent } from "@oh-my-pi/pi-ai";
|
|
11
|
+
import { compactImageMarkers, formatVisionMarker, PLACEHOLDER_REGEX } from "../prompt/composer-attachments";
|
|
12
|
+
import { extractImagePastePathsFromText } from "../prompt/custom-editor";
|
|
10
13
|
import { Editor, type Focusable, matchesKey, Spacer, Text, type TUI } from "../index";
|
|
11
14
|
import { BracketedPasteHandler } from "../bracketed-paste";
|
|
12
15
|
import { getEditorTheme, theme } from "../theme/theme";
|
|
@@ -16,12 +19,32 @@ import { OverlayPanel } from "../chrome/overlay-box";
|
|
|
16
19
|
import { FormField } from "../components/form";
|
|
17
20
|
import { formatKeyHint, formatKeyHints } from "../app-keybindings";
|
|
18
21
|
import { boundKeys, editorKey, interruptKey } from "../chrome/keybinding-hints";
|
|
22
|
+
import { node, span } from "../native/describe";
|
|
23
|
+
import type { DescribeContext, NativeChild, NativeNode } from "../native/node";
|
|
24
|
+
import { hintsRow, type NativeHint, overlayCard } from "../native/overlay";
|
|
25
|
+
import { plainText } from "../native/spans";
|
|
19
26
|
|
|
20
27
|
export interface HookEditorOptions {
|
|
21
28
|
/** Edit text with the host's configured external editor. */
|
|
22
29
|
externalEditor?: (text: string) => Promise<string | null>;
|
|
23
30
|
/** When true, use prompt-style keybindings with the legacy ask prompt chrome. */
|
|
24
31
|
promptStyle?: boolean;
|
|
32
|
+
/** Allow clipboard images to be attached to this prompt. */
|
|
33
|
+
acceptImages?: boolean;
|
|
34
|
+
/** Images already represented by markers in the prefilled text. */
|
|
35
|
+
images?: readonly ImageContent[];
|
|
36
|
+
/**
|
|
37
|
+
* With `acceptImages`, called for an empty bracketed paste, which is how terminals that own
|
|
38
|
+
* the paste key send an image-only clipboard (#3601). Same signature as
|
|
39
|
+
* `CustomEditor.onPasteImage`; the host calls `beginPaste` before its first await.
|
|
40
|
+
*/
|
|
41
|
+
onPasteImage?: () => Promise<boolean>;
|
|
42
|
+
/**
|
|
43
|
+
* With `acceptImages`, called for each path of a bracketed paste made only of image paths.
|
|
44
|
+
* Same signature as `CustomEditor.onPasteImagePath`; calls are not awaited in turn, so images
|
|
45
|
+
* attach as they finish loading and are renumbered in text order on submit.
|
|
46
|
+
*/
|
|
47
|
+
onPasteImagePath?: (path: string) => void | Promise<void>;
|
|
25
48
|
/**
|
|
26
49
|
* Max rows the inner Editor may occupy. When omitted, the editor is
|
|
27
50
|
* bounded to the current terminal height minus the component's chrome
|
|
@@ -35,15 +58,21 @@ export interface HookEditorOptions {
|
|
|
35
58
|
export class HookEditorComponent extends OverlayPanel implements Focusable {
|
|
36
59
|
#editor: Editor;
|
|
37
60
|
#field: FormField;
|
|
38
|
-
#onSubmitCallback: (value: string) => void;
|
|
61
|
+
#onSubmitCallback: (value: string, images?: ImageContent[]) => void;
|
|
39
62
|
#onCancelCallback: () => void;
|
|
40
63
|
#tui: TUI;
|
|
41
64
|
#promptStyle: boolean;
|
|
65
|
+
#acceptImages: boolean;
|
|
66
|
+
#images: ImageContent[];
|
|
42
67
|
#externalEditor: HookEditorOptions["externalEditor"];
|
|
68
|
+
#onPasteImage: HookEditorOptions["onPasteImage"];
|
|
69
|
+
#onPasteImagePath: HookEditorOptions["onPasteImagePath"];
|
|
43
70
|
#pasteHandler = new BracketedPasteHandler();
|
|
44
71
|
#pendingPastes: { settled: boolean; text: string | undefined }[] = [];
|
|
45
72
|
#submitQueued = false;
|
|
46
73
|
#disposed = false;
|
|
74
|
+
/** Detail lines, hints and the editor under the title; every part is fixed, so it is built once. */
|
|
75
|
+
#nativeRoot: NativeNode;
|
|
47
76
|
/** Focus state mirrored to the nested editor during rendering. */
|
|
48
77
|
focused = false;
|
|
49
78
|
|
|
@@ -51,7 +80,7 @@ export class HookEditorComponent extends OverlayPanel implements Focusable {
|
|
|
51
80
|
tui: TUI,
|
|
52
81
|
title: string,
|
|
53
82
|
prefill: string | undefined,
|
|
54
|
-
onSubmit: (value: string) => void,
|
|
83
|
+
onSubmit: (value: string, images?: ImageContent[]) => void,
|
|
55
84
|
onCancel: () => void,
|
|
56
85
|
options?: HookEditorOptions,
|
|
57
86
|
) {
|
|
@@ -59,13 +88,17 @@ export class HookEditorComponent extends OverlayPanel implements Focusable {
|
|
|
59
88
|
// bounded ask question under "◆ Other (type your own)") stay as body rows
|
|
60
89
|
// so they are never truncated into the one-row border.
|
|
61
90
|
const [titleLine = "", ...detailLines] = title.split("\n");
|
|
62
|
-
super(titleLine);
|
|
91
|
+
super(titleLine, "omp.overlay.hook-editor");
|
|
63
92
|
|
|
64
93
|
this.#tui = tui;
|
|
65
94
|
this.#onSubmitCallback = onSubmit;
|
|
66
95
|
this.#onCancelCallback = onCancel;
|
|
67
96
|
this.#promptStyle = options?.promptStyle ?? false;
|
|
97
|
+
this.#acceptImages = options?.acceptImages ?? false;
|
|
98
|
+
this.#images = this.#acceptImages ? [...(options?.images ?? [])] : [];
|
|
68
99
|
this.#externalEditor = options?.externalEditor;
|
|
100
|
+
this.#onPasteImage = options?.onPasteImage;
|
|
101
|
+
this.#onPasteImagePath = options?.onPasteImagePath;
|
|
69
102
|
|
|
70
103
|
// Editor
|
|
71
104
|
this.#editor = new Editor(getEditorTheme());
|
|
@@ -74,6 +107,8 @@ export class HookEditorComponent extends OverlayPanel implements Focusable {
|
|
|
74
107
|
this.#editor.setPromptGutter("> ");
|
|
75
108
|
this.#editor.disableSubmit = true;
|
|
76
109
|
}
|
|
110
|
+
// Image markers delete as a unit, like the main editor's attachment tokens.
|
|
111
|
+
if (options?.acceptImages) this.#editor.atomicTokenPattern = PLACEHOLDER_REGEX;
|
|
77
112
|
// Bound the editor so long content scrolls instead of pushing the
|
|
78
113
|
// submit hint off-screen. Caller may override via options.maxHeight.
|
|
79
114
|
const termRows = this.#tui.terminal?.rows ?? process.stdout.rows ?? 40;
|
|
@@ -99,6 +134,41 @@ export class HookEditorComponent extends OverlayPanel implements Focusable {
|
|
|
99
134
|
});
|
|
100
135
|
this.addChild(this.#field);
|
|
101
136
|
this.addChild(new Spacer(1));
|
|
137
|
+
|
|
138
|
+
const externalHint: NativeHint = {
|
|
139
|
+
keys: boundKeys("app.editor.external", ["ctrl+g"]).slice(0, 1),
|
|
140
|
+
label: "external editor",
|
|
141
|
+
};
|
|
142
|
+
const nativeHints: NativeHint[] = this.#promptStyle
|
|
143
|
+
? [
|
|
144
|
+
{ keys: ["enter", primaryFollowUpKey], label: "submit" },
|
|
145
|
+
{ keys: ["escape"], label: "cancel" },
|
|
146
|
+
externalHint,
|
|
147
|
+
]
|
|
148
|
+
: [
|
|
149
|
+
{ keys: followUpKeys, label: "submit" },
|
|
150
|
+
{ keys: boundKeys("app.interrupt", ["escape"]).slice(0, 1), label: "cancel" },
|
|
151
|
+
externalHint,
|
|
152
|
+
];
|
|
153
|
+
const nativeChildren: NativeChild[] = [];
|
|
154
|
+
if (detailLines.length > 0) {
|
|
155
|
+
nativeChildren.push(
|
|
156
|
+
node(
|
|
157
|
+
"text",
|
|
158
|
+
{ spans: [span(plainText(detailLines.join("\n")), "accent")], wrap: "word" },
|
|
159
|
+
undefined,
|
|
160
|
+
"detail",
|
|
161
|
+
),
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
nativeChildren.push(this.#editor, hintsRow(nativeHints));
|
|
165
|
+
this.#nativeRoot = overlayCard(this.nativeRole, plainText(titleLine), nativeChildren);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/** The editor (which describes itself) under the title and detail lines, with the key hints below. */
|
|
169
|
+
override describe(_cx: DescribeContext): NativeNode {
|
|
170
|
+
this.#field.focused = this.focused;
|
|
171
|
+
return this.#nativeRoot;
|
|
102
172
|
}
|
|
103
173
|
|
|
104
174
|
/** Keep the nested editor's software/hardware cursor mode aligned with the dialog focus target. */
|
|
@@ -117,7 +187,20 @@ export class HookEditorComponent extends OverlayPanel implements Focusable {
|
|
|
117
187
|
const paste = this.#pasteHandler.process(keyData);
|
|
118
188
|
if (paste.handled) {
|
|
119
189
|
if (paste.pasteContent === undefined) return;
|
|
120
|
-
|
|
190
|
+
// Hosts reserve ordered delivery synchronously inside these callbacks, so input
|
|
191
|
+
// that followed the paste in this chunk (e.g. Enter) still waits for the image.
|
|
192
|
+
const content = paste.pasteContent;
|
|
193
|
+
const acceptsImages = this.acceptsImages;
|
|
194
|
+
if (acceptsImages && content.length === 0 && this.#onPasteImage) {
|
|
195
|
+
void this.#onPasteImage();
|
|
196
|
+
} else {
|
|
197
|
+
const imagePaths = acceptsImages ? extractImagePastePathsFromText(content) : undefined;
|
|
198
|
+
if (imagePaths && this.#onPasteImagePath) {
|
|
199
|
+
for (const path of imagePaths) void this.#onPasteImagePath(path);
|
|
200
|
+
} else {
|
|
201
|
+
this.pasteText(content);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
121
204
|
if (paste.remaining.length > 0) this.handleInput(paste.remaining);
|
|
122
205
|
return;
|
|
123
206
|
}
|
|
@@ -134,10 +217,34 @@ export class HookEditorComponent extends OverlayPanel implements Focusable {
|
|
|
134
217
|
this.#submitQueued = true;
|
|
135
218
|
return;
|
|
136
219
|
}
|
|
137
|
-
|
|
220
|
+
let text = this.#editor.getExpandedText();
|
|
138
221
|
if (requireText && text.trim().length === 0) return;
|
|
222
|
+
let images: ImageContent[] | undefined;
|
|
223
|
+
if (this.#images.length > 0) {
|
|
224
|
+
// Path pastes attach as they finish loading; submit numbers images in text order.
|
|
225
|
+
const compacted = compactImageMarkers(text, this.#images.length, { byAppearance: true });
|
|
226
|
+
if (compacted) {
|
|
227
|
+
text = compacted.text;
|
|
228
|
+
images = compacted.keep.map(index => this.#images[index]);
|
|
229
|
+
} else {
|
|
230
|
+
images = this.#images;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
139
233
|
this.dispose();
|
|
140
|
-
this.#onSubmitCallback(text);
|
|
234
|
+
if (images?.length) this.#onSubmitCallback(text, images);
|
|
235
|
+
else this.#onSubmitCallback(text);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/** Whether this prompt opted into clipboard image attachments. */
|
|
239
|
+
get acceptsImages(): boolean {
|
|
240
|
+
return this.#acceptImages && !this.#disposed;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/** Attach an image; returns its `[Image #N, WxH]` marker for the caller to deliver. */
|
|
244
|
+
attachImage(image: ImageContent, dims?: { width: number; height: number }): string | undefined {
|
|
245
|
+
if (!this.acceptsImages) return undefined;
|
|
246
|
+
this.#images.push(image);
|
|
247
|
+
return formatVisionMarker("image", this.#images.length, dims);
|
|
141
248
|
}
|
|
142
249
|
|
|
143
250
|
/** Reserve ordered clipboard delivery. Completion accepts nonempty text once, or releases on undefined. */
|
|
@@ -8,6 +8,10 @@ import { formTheme } from "../chrome/form-theme";
|
|
|
8
8
|
import { OverlayPanel } from "../chrome/overlay-box";
|
|
9
9
|
import { Form, TextFormField } from "../components/form";
|
|
10
10
|
import { editorKey, interruptKey } from "../chrome/keybinding-hints";
|
|
11
|
+
import { node } from "../native/describe";
|
|
12
|
+
import type { DescribeContext, NativeNode } from "../native/node";
|
|
13
|
+
import { overlayCard } from "../native/overlay";
|
|
14
|
+
import { plainText } from "../native/spans";
|
|
11
15
|
|
|
12
16
|
export interface HookInputOptions {
|
|
13
17
|
tui?: TUI;
|
|
@@ -22,6 +26,7 @@ export class HookInputComponent extends OverlayPanel {
|
|
|
22
26
|
#onCancelCallback: () => void;
|
|
23
27
|
#baseTitle: string;
|
|
24
28
|
#countdown: CountdownTimer | undefined;
|
|
29
|
+
#nativeMemo: { countdown: NativeNode | undefined; node: NativeNode } | undefined;
|
|
25
30
|
|
|
26
31
|
constructor(
|
|
27
32
|
title: string,
|
|
@@ -30,7 +35,7 @@ export class HookInputComponent extends OverlayPanel {
|
|
|
30
35
|
onCancel: () => void,
|
|
31
36
|
opts?: HookInputOptions,
|
|
32
37
|
) {
|
|
33
|
-
super(title);
|
|
38
|
+
super(title, "omp.overlay.hook-input");
|
|
34
39
|
|
|
35
40
|
this.#onSubmitCallback = onSubmit;
|
|
36
41
|
this.#onCancelCallback = onCancel;
|
|
@@ -82,4 +87,20 @@ export class HookInputComponent extends OverlayPanel {
|
|
|
82
87
|
this.#countdown?.dispose();
|
|
83
88
|
super.dispose();
|
|
84
89
|
}
|
|
90
|
+
|
|
91
|
+
/** A card over the form, headed by the title plus the countdown `elapsed` while a timeout runs. */
|
|
92
|
+
override describe(_cx: DescribeContext): NativeNode {
|
|
93
|
+
const countdown = this.#countdown?.describe();
|
|
94
|
+
const memo = this.#nativeMemo;
|
|
95
|
+
if (memo && memo.countdown === countdown) return memo.node;
|
|
96
|
+
const title = plainText(this.#baseTitle);
|
|
97
|
+
const root = countdown
|
|
98
|
+
? overlayCard(this.nativeRole, undefined, [
|
|
99
|
+
node("row", { gap: "sm", align: "baseline" }, [node("text", { text: title }), countdown], "head"),
|
|
100
|
+
this.#form,
|
|
101
|
+
])
|
|
102
|
+
: overlayCard(this.nativeRole, title, [this.#form]);
|
|
103
|
+
this.#nativeMemo = { countdown, node: root };
|
|
104
|
+
return root;
|
|
105
|
+
}
|
|
85
106
|
}
|