@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
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ImageContent } from "@oh-my-pi/pi-ai";
|
|
1
2
|
import {
|
|
2
3
|
type Component,
|
|
3
4
|
Ellipsis,
|
|
@@ -33,6 +34,12 @@ export interface ExtensionAskDialogQuestion {
|
|
|
33
34
|
recommended?: number;
|
|
34
35
|
}
|
|
35
36
|
|
|
37
|
+
/** Prompt text and the images pasted into it (custom answers and notes). */
|
|
38
|
+
export interface AskDialogPromptValue {
|
|
39
|
+
text: string;
|
|
40
|
+
images?: ImageContent[];
|
|
41
|
+
}
|
|
42
|
+
|
|
36
43
|
/** Submitted answer to one dialog question. */
|
|
37
44
|
export interface ExtensionAskDialogResultItem {
|
|
38
45
|
id: string;
|
|
@@ -41,7 +48,9 @@ export interface ExtensionAskDialogResultItem {
|
|
|
41
48
|
multi: boolean;
|
|
42
49
|
selectedOptions: string[];
|
|
43
50
|
customInput?: string;
|
|
51
|
+
customInputImages?: ImageContent[];
|
|
44
52
|
note?: string;
|
|
53
|
+
noteImages?: ImageContent[];
|
|
45
54
|
timedOut?: boolean;
|
|
46
55
|
}
|
|
47
56
|
|
|
@@ -75,9 +84,25 @@ import { editorKey, editorKeys } from "../chrome/keybinding-hints";
|
|
|
75
84
|
import { formatKeyHint, formatKeyHints } from "../app-keybindings";
|
|
76
85
|
import { OverlayPanel, PanelDivider, PanelRows } from "../chrome/overlay-box";
|
|
77
86
|
import { handleTabSwitchKey } from "../chrome/selector-helpers";
|
|
87
|
+
import { col, node, span, text } from "../native/describe";
|
|
88
|
+
import {
|
|
89
|
+
type DescribeContext,
|
|
90
|
+
leafKey,
|
|
91
|
+
type NativeChild,
|
|
92
|
+
type NativeNode,
|
|
93
|
+
type NativeUiEvent,
|
|
94
|
+
type TspSpan,
|
|
95
|
+
} from "../native/node";
|
|
96
|
+
import { actionBar, actionButton } from "../native/overlay";
|
|
97
|
+
import { getKeybindings } from "../keybindings";
|
|
98
|
+
import { plainText } from "../native/spans";
|
|
78
99
|
|
|
79
100
|
const OTHER_OPTION = "Other (type your own)";
|
|
80
101
|
const SUBMIT_OPTION = "Submit";
|
|
102
|
+
const RECOMMENDED_SUFFIX = " (Recommended)";
|
|
103
|
+
/** Native labels: the free-text row and the answers-review tab. */
|
|
104
|
+
const OTHER_LABEL = "Other…";
|
|
105
|
+
const REVIEW_TAB = "Review";
|
|
81
106
|
|
|
82
107
|
// Action rows appended by the guest race participant. An option sanitizing
|
|
83
108
|
// to one of these must disambiguate identically on both sides, or the same
|
|
@@ -129,6 +154,8 @@ interface AskDialogCallbacks {
|
|
|
129
154
|
onSubmit(result: ExtensionAskDialogSubmitResult): void;
|
|
130
155
|
onCancel(): void;
|
|
131
156
|
onPrompt(title: string, prefill?: string): Promise<string | undefined>;
|
|
157
|
+
/** Prompt that accepts pasted images; without it, prompts use `onPrompt`. */
|
|
158
|
+
onImagePrompt?(title: string, prefill: AskDialogPromptValue | undefined): Promise<AskDialogPromptValue | undefined>;
|
|
132
159
|
}
|
|
133
160
|
|
|
134
161
|
interface AskDialogInputGuard {
|
|
@@ -151,7 +178,9 @@ interface AskDialogOptions {
|
|
|
151
178
|
interface QuestionState {
|
|
152
179
|
selectedOptions: Set<string>;
|
|
153
180
|
customInput: string | undefined;
|
|
181
|
+
customInputImages: ImageContent[] | undefined;
|
|
154
182
|
note: string | undefined;
|
|
183
|
+
noteImages: ImageContent[] | undefined;
|
|
155
184
|
noteRowKey: string | undefined;
|
|
156
185
|
cursorIndex: number;
|
|
157
186
|
scrollOffset: number;
|
|
@@ -186,9 +215,13 @@ function clamp(value: number, min: number, max: number): number {
|
|
|
186
215
|
return Math.max(min, Math.min(value, max));
|
|
187
216
|
}
|
|
188
217
|
|
|
218
|
+
/** Full tab label; the ANSI tab strip and review list clip it to a chip. */
|
|
219
|
+
function questionTabName(question: ExtensionAskDialogQuestion, index: number): string {
|
|
220
|
+
return replaceTabs(question.header?.trim() || sanitizeCarriageReturns(question.id) || `Q${index + 1}`);
|
|
221
|
+
}
|
|
222
|
+
|
|
189
223
|
function questionTabLabel(question: ExtensionAskDialogQuestion, index: number): string {
|
|
190
|
-
|
|
191
|
-
return truncateToWidth(replaceTabs(base), MAX_HEADER_CHIP_WIDTH, Ellipsis.Unicode);
|
|
224
|
+
return truncateToWidth(questionTabName(question, index), MAX_HEADER_CHIP_WIDTH, Ellipsis.Unicode);
|
|
192
225
|
}
|
|
193
226
|
|
|
194
227
|
function wrapQuestionTitle(question: ExtensionAskDialogQuestion, width: number): string[] {
|
|
@@ -323,10 +356,11 @@ function normalizedInlineInput(input: string): string {
|
|
|
323
356
|
* participant answers. State and results keep originals.
|
|
324
357
|
*/
|
|
325
358
|
function displayOptionLabels(question: ExtensionAskDialogQuestion): string[] {
|
|
326
|
-
const recommendedSuffix = " (Recommended)";
|
|
327
359
|
const badged = question.options.map((option, index) => {
|
|
328
360
|
const base = sanitizeCarriageReturns(option.label);
|
|
329
|
-
return question.recommended === index && !base.endsWith(
|
|
361
|
+
return question.recommended === index && !base.endsWith(RECOMMENDED_SUFFIX)
|
|
362
|
+
? `${base}${RECOMMENDED_SUFFIX}`
|
|
363
|
+
: base;
|
|
330
364
|
});
|
|
331
365
|
return disambiguateDisplayLabels(badged, [OTHER_OPTION, ...GUEST_ACTION_LABELS]);
|
|
332
366
|
}
|
|
@@ -350,8 +384,43 @@ function renderAnswerSummary(question: ExtensionAskDialogQuestion, state: Questi
|
|
|
350
384
|
return selected[0] ?? theme.fg("warning", "unanswered");
|
|
351
385
|
}
|
|
352
386
|
|
|
387
|
+
/** {@link renderAnswerSummary} as spans for the native review list. */
|
|
388
|
+
function describeAnswer(question: ExtensionAskDialogQuestion, state: QuestionState): readonly TspSpan[] {
|
|
389
|
+
if (state.selectedOptions.size === 0 && state.customInput === undefined) return [span("unanswered", "warning")];
|
|
390
|
+
const spans = [span(plainText(renderAnswerSummary(question, state)))];
|
|
391
|
+
const note = noteForSubmittedAnswer(question, state)?.trim();
|
|
392
|
+
if (note) spans.push(span(` · Note: ${normalizedInlineInput(note)}`, "muted"));
|
|
393
|
+
return spans;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/** Native `item.detail` of an option row: its description, the typed custom answer, and its note. */
|
|
397
|
+
function describeRowDetail(
|
|
398
|
+
rowItem: QuestionRow,
|
|
399
|
+
question: ExtensionAskDialogQuestion,
|
|
400
|
+
state: QuestionState,
|
|
401
|
+
): readonly TspSpan[] | undefined {
|
|
402
|
+
const spans: TspSpan[] = [];
|
|
403
|
+
if (rowItem.kind === "option") {
|
|
404
|
+
const description = question.options[rowItem.optionIndex ?? -1]?.description?.trim();
|
|
405
|
+
if (description) spans.push(span(normalizedInlineInput(description), "muted"));
|
|
406
|
+
} else if (state.customInput !== undefined) {
|
|
407
|
+
spans.push(span(`“${normalizedInlineInput(state.customInput)}”`, "muted"));
|
|
408
|
+
}
|
|
409
|
+
if (state.note && state.noteRowKey === rowItem.key) {
|
|
410
|
+
spans.push(span(`${spans.length > 0 ? " · " : ""}✎ ${normalizedInlineInput(state.note)}`, "success"));
|
|
411
|
+
}
|
|
412
|
+
return spans.length > 0 ? spans : undefined;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/** Normalize the result of either prompt callback. */
|
|
416
|
+
function splitPromptInput(input: string | AskDialogPromptValue): { text: string; images: ImageContent[] | undefined } {
|
|
417
|
+
if (typeof input === "string") return { text: input, images: undefined };
|
|
418
|
+
return { text: input.text, images: input.images?.length ? input.images : undefined };
|
|
419
|
+
}
|
|
420
|
+
|
|
353
421
|
function clearNote(state: QuestionState): void {
|
|
354
422
|
state.note = undefined;
|
|
423
|
+
state.noteImages = undefined;
|
|
355
424
|
state.noteRowKey = undefined;
|
|
356
425
|
}
|
|
357
426
|
|
|
@@ -490,6 +559,14 @@ export class AskDialogComponent implements Component {
|
|
|
490
559
|
#contentWidth = 76;
|
|
491
560
|
#headerExpandable = false;
|
|
492
561
|
#descExpandable = false;
|
|
562
|
+
/** Memoized native description; dropped whenever dialog state may have changed. */
|
|
563
|
+
#native: NativeNode | undefined;
|
|
564
|
+
/** Input-guard blocked state {@link #native} was described with (the guard flips outside this dialog). */
|
|
565
|
+
#nativeBlocked = false;
|
|
566
|
+
/** When the countdown runs out (epoch ms), for the native ring. */
|
|
567
|
+
#countdownDeadline = 0;
|
|
568
|
+
/** Once-a-second re-describe while the native countdown ring is shown. */
|
|
569
|
+
#ringTick: NodeJS.Timeout | undefined;
|
|
493
570
|
readonly #panel: OverlayPanel;
|
|
494
571
|
readonly #headerRegion: PanelRows;
|
|
495
572
|
readonly #bodyRegion: PanelRows;
|
|
@@ -508,7 +585,9 @@ export class AskDialogComponent implements Component {
|
|
|
508
585
|
return {
|
|
509
586
|
selectedOptions: new Set<string>(),
|
|
510
587
|
customInput: undefined,
|
|
588
|
+
customInputImages: undefined,
|
|
511
589
|
note: undefined,
|
|
590
|
+
noteImages: undefined,
|
|
512
591
|
noteRowKey: undefined,
|
|
513
592
|
cursorIndex: clamp(recommended ?? 0, 0, maxIndex),
|
|
514
593
|
scrollOffset: 0,
|
|
@@ -522,6 +601,8 @@ export class AskDialogComponent implements Component {
|
|
|
522
601
|
options.tui,
|
|
523
602
|
seconds => {
|
|
524
603
|
this.#remainingSeconds = seconds;
|
|
604
|
+
// Fires on every (re)start: the native ring's deadline.
|
|
605
|
+
this.#countdownDeadline = Date.now() + seconds * 1000;
|
|
525
606
|
},
|
|
526
607
|
() => this.#handleTimeout(),
|
|
527
608
|
);
|
|
@@ -539,6 +620,7 @@ export class AskDialogComponent implements Component {
|
|
|
539
620
|
}
|
|
540
621
|
|
|
541
622
|
invalidate(): void {
|
|
623
|
+
this.#native = undefined;
|
|
542
624
|
this.#stableHeight = undefined;
|
|
543
625
|
this.#previewCache.clear();
|
|
544
626
|
this.#overflowLayouts = new WeakMap();
|
|
@@ -549,6 +631,7 @@ export class AskDialogComponent implements Component {
|
|
|
549
631
|
dispose(): void {
|
|
550
632
|
this.#closed = true;
|
|
551
633
|
this.#countdown?.dispose();
|
|
634
|
+
this.#stopRingTick();
|
|
552
635
|
this.#panel.dispose();
|
|
553
636
|
}
|
|
554
637
|
/**
|
|
@@ -557,23 +640,30 @@ export class AskDialogComponent implements Component {
|
|
|
557
640
|
* expand transcript tools.
|
|
558
641
|
*/
|
|
559
642
|
toggleQuestionExpansion(): boolean {
|
|
560
|
-
if (this.#
|
|
561
|
-
const question = this.#questions[this.#currentQuestionIndex()];
|
|
562
|
-
if (!question) return false;
|
|
563
|
-
const headerOverflows = wrapQuestionTitle(question, this.#contentWidth).length > MAX_HEADER_ROWS;
|
|
564
|
-
const descOverflows = questionDescriptionsOverflow(question, this.#contentWidth);
|
|
565
|
-
if (!headerOverflows && !descOverflows && !this.#expanded) return false;
|
|
643
|
+
if (!this.#canToggleExpansion()) return false;
|
|
566
644
|
this.#expanded = !this.#expanded;
|
|
567
645
|
this.invalidate();
|
|
568
646
|
this.#requestRender();
|
|
569
647
|
return true;
|
|
570
648
|
}
|
|
571
649
|
|
|
650
|
+
#canToggleExpansion(): boolean {
|
|
651
|
+
if (this.#closed || this.#isSubmitTab()) return false;
|
|
652
|
+
const question = this.#questions[this.#currentQuestionIndex()];
|
|
653
|
+
if (!question) return false;
|
|
654
|
+
if (this.#expanded) return true;
|
|
655
|
+
return (
|
|
656
|
+
wrapQuestionTitle(question, this.#contentWidth).length > MAX_HEADER_ROWS ||
|
|
657
|
+
questionDescriptionsOverflow(question, this.#contentWidth)
|
|
658
|
+
);
|
|
659
|
+
}
|
|
660
|
+
|
|
572
661
|
handleInput(keyData: string): void {
|
|
573
662
|
if (this.#closed || this.#promptActive) return;
|
|
574
663
|
// Reset the inactivity countdown on any key that reaches past the
|
|
575
664
|
// closed/prompt guards, matching HookSelector/HookInput semantics.
|
|
576
665
|
this.#countdown?.reset();
|
|
666
|
+
this.#native = undefined;
|
|
577
667
|
if (matchesSelectCancel(keyData)) {
|
|
578
668
|
this.#finishCancel();
|
|
579
669
|
return;
|
|
@@ -636,6 +726,291 @@ export class AskDialogComponent implements Component {
|
|
|
636
726
|
return this.#panel.render(width);
|
|
637
727
|
}
|
|
638
728
|
|
|
729
|
+
/**
|
|
730
|
+
* A bottom-anchored glass sheet over the composer (the `overlay` hoists into
|
|
731
|
+
* the terminal's layer; the dialog's own slot in the dock stays empty).
|
|
732
|
+
*/
|
|
733
|
+
describe(cx: DescribeContext): NativeNode {
|
|
734
|
+
const inputGuard = this.options.inputGuard;
|
|
735
|
+
// Same draft-cursor mirroring as render(): the proxied draft describes itself next to this dialog.
|
|
736
|
+
inputGuard?.syncPresentation?.();
|
|
737
|
+
const blocked = inputGuard?.isBlocked() ?? false;
|
|
738
|
+
const ring = this.#countdown !== undefined && cx.supports("meter");
|
|
739
|
+
if (ring) this.#startRingTick();
|
|
740
|
+
if (this.#native && this.#nativeBlocked === blocked) return this.#native;
|
|
741
|
+
const children: NativeChild[] = [];
|
|
742
|
+
const head = this.#describeHead(ring);
|
|
743
|
+
if (head) children.push(head);
|
|
744
|
+
if (this.#isSubmitTab()) this.#describeSubmitBody(children);
|
|
745
|
+
else this.#describeQuestionBody(children);
|
|
746
|
+
children.push(this.#describeActions(blocked));
|
|
747
|
+
const sheet = node(
|
|
748
|
+
"overlay",
|
|
749
|
+
{ role: "omp.overlay.ask", anchor: "bottom", size: "md", modal: true },
|
|
750
|
+
[col(children, { gap: "md" })],
|
|
751
|
+
"sheet",
|
|
752
|
+
);
|
|
753
|
+
this.#native = col([sheet]);
|
|
754
|
+
this.#nativeBlocked = blocked;
|
|
755
|
+
return this.#native;
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
/** Question tabs (plus Review) and the countdown: a ring when the terminal draws meters, else a clocked `elapsed`. */
|
|
759
|
+
#describeHead(ring: boolean): NativeNode | undefined {
|
|
760
|
+
const children: NativeChild[] = [];
|
|
761
|
+
if (this.#hasSubmitTab()) {
|
|
762
|
+
const items = this.#questions.map((question, index) => ({
|
|
763
|
+
id: String(index),
|
|
764
|
+
label: questionTabName(question, index),
|
|
765
|
+
}));
|
|
766
|
+
items.push({ id: "submit", label: REVIEW_TAB });
|
|
767
|
+
const active = this.#isSubmitTab() ? "submit" : String(this.#activeTabIndex);
|
|
768
|
+
children.push(
|
|
769
|
+
node("tabs", { items, active, role: "omp.ask.questions", actions: { click: "select" } }, undefined, "tabs"),
|
|
770
|
+
);
|
|
771
|
+
}
|
|
772
|
+
if (this.#countdown) {
|
|
773
|
+
children.push(node("spacer", { grow: 1 }));
|
|
774
|
+
if (ring) {
|
|
775
|
+
const total = Math.max(1, this.options.timeout ?? 1);
|
|
776
|
+
const left = Math.max(0, this.#countdownDeadline - Date.now());
|
|
777
|
+
children.push(
|
|
778
|
+
node(
|
|
779
|
+
"meter",
|
|
780
|
+
{
|
|
781
|
+
value: Math.round((left / total) * 1000) / 1000,
|
|
782
|
+
style: "ring",
|
|
783
|
+
size: "sm",
|
|
784
|
+
label: `${Math.ceil(left / 1000)}s`,
|
|
785
|
+
title: "Answers the recommended option when the time runs out",
|
|
786
|
+
},
|
|
787
|
+
undefined,
|
|
788
|
+
"timer",
|
|
789
|
+
),
|
|
790
|
+
);
|
|
791
|
+
} else {
|
|
792
|
+
children.push(this.#countdown.describe());
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
if (children.length === 0) return undefined;
|
|
796
|
+
return node("row", { role: "omp.ask.head", gap: "sm", align: "center" }, children, "head");
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
/** Question tab: the question as markdown and its answers as radio/check rows (label, description, badge). */
|
|
800
|
+
#describeQuestionBody(children: NativeChild[]): void {
|
|
801
|
+
const active = this.#activeQuestionState();
|
|
802
|
+
if (!active) return;
|
|
803
|
+
const { question, state } = active;
|
|
804
|
+
const rows = this.#questionRows(question);
|
|
805
|
+
state.cursorIndex = clamp(state.cursorIndex, 0, Math.max(0, rows.length - 1));
|
|
806
|
+
children.push(
|
|
807
|
+
node(
|
|
808
|
+
"md",
|
|
809
|
+
{ text: replaceTabs(sanitizeCarriageReturns(question.question)), role: "omp.ask.question" },
|
|
810
|
+
undefined,
|
|
811
|
+
"question",
|
|
812
|
+
),
|
|
813
|
+
);
|
|
814
|
+
const optionRole = question.multi ? "omp.ask.check" : "omp.ask.option";
|
|
815
|
+
const items = rows.map(rowItem => {
|
|
816
|
+
const option = rowItem.kind === "option" ? question.options[rowItem.optionIndex ?? -1] : undefined;
|
|
817
|
+
const checked =
|
|
818
|
+
rowItem.kind === "other"
|
|
819
|
+
? state.customInput !== undefined
|
|
820
|
+
: option !== undefined && state.selectedOptions.has(option.label);
|
|
821
|
+
const recommended = rowItem.kind === "option" && question.recommended === rowItem.optionIndex;
|
|
822
|
+
// The badge replaces the ANSI `(Recommended)` suffix.
|
|
823
|
+
const label =
|
|
824
|
+
rowItem.kind === "other"
|
|
825
|
+
? OTHER_LABEL
|
|
826
|
+
: recommended && rowItem.label.endsWith(RECOMMENDED_SUFFIX)
|
|
827
|
+
? rowItem.label.slice(0, -RECOMMENDED_SUFFIX.length)
|
|
828
|
+
: rowItem.label;
|
|
829
|
+
const detail =
|
|
830
|
+
describeRowDetail(rowItem, question, state) ??
|
|
831
|
+
(rowItem.kind === "other" ? [span("Type your own answer", "dim")] : undefined);
|
|
832
|
+
return node(
|
|
833
|
+
"item",
|
|
834
|
+
{
|
|
835
|
+
// Inline code runs mono, as the ANSI row renders inline markdown.
|
|
836
|
+
label: label
|
|
837
|
+
.split(/(`[^`]+`)/)
|
|
838
|
+
.flatMap(piece =>
|
|
839
|
+
piece === ""
|
|
840
|
+
? []
|
|
841
|
+
: piece.length > 2 && piece.startsWith("`") && piece.endsWith("`")
|
|
842
|
+
? [span(piece.slice(1, -1), "code")]
|
|
843
|
+
: [span(piece)],
|
|
844
|
+
),
|
|
845
|
+
role: rowItem.kind === "other" ? "omp.ask.other" : optionRole,
|
|
846
|
+
...(detail ? { detail } : {}),
|
|
847
|
+
...(recommended ? { value: [span("Recommended", "accent")] } : {}),
|
|
848
|
+
...(checked ? { icon: "check", tone: "success" as const } : {}),
|
|
849
|
+
},
|
|
850
|
+
undefined,
|
|
851
|
+
rowItem.key,
|
|
852
|
+
);
|
|
853
|
+
});
|
|
854
|
+
// Keyed per question: a click aimed at one question's list never lands on the next after it advances.
|
|
855
|
+
children.push(
|
|
856
|
+
node(
|
|
857
|
+
"list",
|
|
858
|
+
{ selected: rows[state.cursorIndex]?.key ?? null, role: "omp.ask.options" },
|
|
859
|
+
items,
|
|
860
|
+
`q${this.#currentQuestionIndex()}`,
|
|
861
|
+
),
|
|
862
|
+
);
|
|
863
|
+
const highlighted = rows[state.cursorIndex];
|
|
864
|
+
const option = highlighted?.kind === "option" ? question.options[highlighted.optionIndex ?? -1] : undefined;
|
|
865
|
+
if (option?.preview?.trim()) {
|
|
866
|
+
children.push(node("md", { text: replaceTabs(option.preview) }, undefined, "preview"));
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
/** Review tab: every answer as key/value pairs; the action bar's Submit sends them. */
|
|
871
|
+
#describeSubmitBody(children: NativeChild[]): void {
|
|
872
|
+
const unanswered = this.#unansweredCount();
|
|
873
|
+
if (unanswered > 0) {
|
|
874
|
+
const noun = `question${unanswered === 1 ? "" : "s"}`;
|
|
875
|
+
children.push(
|
|
876
|
+
text([span(`${unanswered} unanswered ${noun}; ${formatKeyHint("enter")} still submits.`, "warning")]),
|
|
877
|
+
);
|
|
878
|
+
}
|
|
879
|
+
const answers: { k: string; v: readonly TspSpan[] }[] = [];
|
|
880
|
+
for (let index = 0; index < this.#questions.length; index++) {
|
|
881
|
+
const question = this.#questions[index];
|
|
882
|
+
const state = this.#states[index];
|
|
883
|
+
if (!question || !state) continue;
|
|
884
|
+
answers.push({ k: `${index + 1}. ${questionTabName(question, index)}`, v: describeAnswer(question, state) });
|
|
885
|
+
}
|
|
886
|
+
children.push(node("kv", { items: answers }, undefined, "answers"));
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
/**
|
|
890
|
+
* The action bar: Submit ⏎ (Enter's action on this tab; `Next` while more
|
|
891
|
+
* questions follow), Note n, Skip esc. Keyboard-only keys live in the
|
|
892
|
+
* buttons' tooltips; a draft that owns input shows its hint instead.
|
|
893
|
+
*/
|
|
894
|
+
#describeActions(blocked: boolean): NativeNode {
|
|
895
|
+
const cancelKey = getKeybindings().getKeys("tui.select.cancel")[0];
|
|
896
|
+
const skip = actionButton("Skip", "cancel", cancelKey ? { keys: cancelKey } : {});
|
|
897
|
+
const inputGuard = this.options.inputGuard;
|
|
898
|
+
if (blocked && inputGuard) {
|
|
899
|
+
return actionBar([text([span(plainText(inputGuard.hint), "muted")], { truncate: "end" }), null, skip]);
|
|
900
|
+
}
|
|
901
|
+
const moves = ["up/down move"];
|
|
902
|
+
if (this.#hasSubmitTab()) moves.push("tab switches question");
|
|
903
|
+
if (this.#isSubmitTab()) {
|
|
904
|
+
const submit = actionButton("Submit", "submit", {
|
|
905
|
+
keys: "enter",
|
|
906
|
+
tone: "accent",
|
|
907
|
+
title: "Submit answers enter",
|
|
908
|
+
});
|
|
909
|
+
return actionBar([null, skip, submit]);
|
|
910
|
+
}
|
|
911
|
+
const question = this.#questions[this.#currentQuestionIndex()];
|
|
912
|
+
const last = this.#questions.length === 1;
|
|
913
|
+
const enterLabel = last ? "Submit" : "Next";
|
|
914
|
+
const tips = question?.multi ? ["space toggles", ...moves] : moves;
|
|
915
|
+
const submit = actionButton(enterLabel, "submit", {
|
|
916
|
+
keys: "enter",
|
|
917
|
+
tone: "accent",
|
|
918
|
+
title: `${enterLabel} enter · ${tips.join(" · ")}`,
|
|
919
|
+
});
|
|
920
|
+
const note = actionButton("Note", "note", { keys: "n", title: `Add a note to the highlighted answer n` });
|
|
921
|
+
return actionBar([null, note, skip, submit]);
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
/**
|
|
925
|
+
* Pointer actions: a tab click switches to it; a click on an answer row is
|
|
926
|
+
* Enter on it (Space for a multi-select option, so it toggles; its
|
|
927
|
+
* double-click `activate` is Enter); the action bar's Submit, Note and Skip
|
|
928
|
+
* run exactly what Enter, `n` and Esc run on the highlighted row.
|
|
929
|
+
*/
|
|
930
|
+
handleNativeEvent(event: NativeUiEvent): void {
|
|
931
|
+
if (this.#closed || this.#promptActive) return;
|
|
932
|
+
if (event.type === "action") {
|
|
933
|
+
if (event.act === "cancel") {
|
|
934
|
+
this.#finishCancel();
|
|
935
|
+
return;
|
|
936
|
+
}
|
|
937
|
+
if (this.options.inputGuard?.isBlocked()) return;
|
|
938
|
+
if (event.act === "submit") this.#pressEnter();
|
|
939
|
+
else if (event.act === "note") this.#pressNote();
|
|
940
|
+
return;
|
|
941
|
+
}
|
|
942
|
+
if (event.type !== "select" && event.type !== "activate") return;
|
|
943
|
+
if (this.options.inputGuard?.isBlocked()) return;
|
|
944
|
+
const key = leafKey(event.key);
|
|
945
|
+
if (key === "tabs") {
|
|
946
|
+
if (!this.#hasSubmitTab()) return;
|
|
947
|
+
const index = event.item === "submit" ? this.#submitTabIndex() : Number(event.item);
|
|
948
|
+
if (!Number.isInteger(index) || index < 0 || index > this.#submitTabIndex()) return;
|
|
949
|
+
this.#countdown?.reset();
|
|
950
|
+
this.#activeTabIndex = index;
|
|
951
|
+
this.#submitScrollOffset = 0;
|
|
952
|
+
this.#requestRender();
|
|
953
|
+
return;
|
|
954
|
+
}
|
|
955
|
+
if (this.#isSubmitTab() || key !== `q${this.#currentQuestionIndex()}`) return;
|
|
956
|
+
const active = this.#activeQuestionState();
|
|
957
|
+
if (!active) return;
|
|
958
|
+
const { question, state } = active;
|
|
959
|
+
const rows = this.#questionRows(question);
|
|
960
|
+
const index = rows.findIndex(rowItem => rowItem.key === event.item);
|
|
961
|
+
const rowItem = rows[index];
|
|
962
|
+
if (!rowItem) return;
|
|
963
|
+
this.#countdown?.reset();
|
|
964
|
+
state.cursorIndex = index;
|
|
965
|
+
state.manualScroll = false;
|
|
966
|
+
const toggles = event.type === "select" && question.multi === true && rowItem.kind === "option";
|
|
967
|
+
this.#commitRow(question, state, rowItem, !toggles);
|
|
968
|
+
this.#requestRender();
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
/** Enter: submit on the Review tab, else commit the highlighted row. */
|
|
972
|
+
#pressEnter(): void {
|
|
973
|
+
this.#countdown?.reset();
|
|
974
|
+
if (this.#isSubmitTab()) {
|
|
975
|
+
this.#finishSubmit();
|
|
976
|
+
return;
|
|
977
|
+
}
|
|
978
|
+
const active = this.#activeQuestionState();
|
|
979
|
+
const rowItem = active ? this.#questionRows(active.question)[active.state.cursorIndex] : undefined;
|
|
980
|
+
if (!active || !rowItem) return;
|
|
981
|
+
this.#commitRow(active.question, active.state, rowItem, true);
|
|
982
|
+
this.#requestRender();
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
/** `n`: a note on the highlighted row (questions only). */
|
|
986
|
+
#pressNote(): void {
|
|
987
|
+
if (this.#isSubmitTab()) return;
|
|
988
|
+
this.#countdown?.reset();
|
|
989
|
+
const active = this.#activeQuestionState();
|
|
990
|
+
const rowItem = active ? this.#questionRows(active.question)[active.state.cursorIndex] : undefined;
|
|
991
|
+
if (!active || !rowItem) return;
|
|
992
|
+
void this.#promptForNote(active.question, active.state, rowItem);
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
/** Re-describe once a second while a countdown ring is shown (the ring's value is data, not a terminal clock). */
|
|
996
|
+
#startRingTick(): void {
|
|
997
|
+
if (this.#ringTick || this.#closed) return;
|
|
998
|
+
this.#ringTick = setInterval(() => {
|
|
999
|
+
if (this.#closed) {
|
|
1000
|
+
this.#stopRingTick();
|
|
1001
|
+
return;
|
|
1002
|
+
}
|
|
1003
|
+
this.#requestRender();
|
|
1004
|
+
}, 1000);
|
|
1005
|
+
this.#ringTick.unref?.();
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
#stopRingTick(): void {
|
|
1009
|
+
if (!this.#ringTick) return;
|
|
1010
|
+
clearInterval(this.#ringTick);
|
|
1011
|
+
this.#ringTick = undefined;
|
|
1012
|
+
}
|
|
1013
|
+
|
|
639
1014
|
#dialogHeight(width: number, termRows: number): number {
|
|
640
1015
|
const key = `${width}:${termRows}:${this.#expanded ? 1 : 0}`;
|
|
641
1016
|
if (this.#stableHeight?.key === key) return this.#stableHeight.total;
|
|
@@ -713,6 +1088,7 @@ export class AskDialogComponent implements Component {
|
|
|
713
1088
|
}
|
|
714
1089
|
|
|
715
1090
|
#requestRender(): void {
|
|
1091
|
+
this.#native = undefined;
|
|
716
1092
|
this.options.tui?.requestRender();
|
|
717
1093
|
}
|
|
718
1094
|
|
|
@@ -841,6 +1217,16 @@ export class AskDialogComponent implements Component {
|
|
|
841
1217
|
const isEnter = matchesKey(keyData, "enter") || matchesKey(keyData, "return") || keyData === "\n";
|
|
842
1218
|
const isSpace = matchesKey(keyData, "space") || keyData === " ";
|
|
843
1219
|
if (!isEnter && !(question.multi && isSpace)) return;
|
|
1220
|
+
this.#commitRow(question, state, rowItem, isEnter);
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
/** Enter (`isEnter`) or multi-select Space on a row: toggle, select, or prompt for its answer. */
|
|
1224
|
+
#commitRow(
|
|
1225
|
+
question: ExtensionAskDialogQuestion,
|
|
1226
|
+
state: QuestionState,
|
|
1227
|
+
rowItem: QuestionRow,
|
|
1228
|
+
isEnter: boolean,
|
|
1229
|
+
): void {
|
|
844
1230
|
if (rowItem.kind === "other") {
|
|
845
1231
|
void this.#promptForCustomInput(question, state, rowItem);
|
|
846
1232
|
return;
|
|
@@ -867,6 +1253,7 @@ export class AskDialogComponent implements Component {
|
|
|
867
1253
|
}
|
|
868
1254
|
state.selectedOptions = new Set([option.label]);
|
|
869
1255
|
state.customInput = undefined;
|
|
1256
|
+
state.customInputImages = undefined;
|
|
870
1257
|
clearNoteUnlessRow(state, rowItem.key);
|
|
871
1258
|
this.#advanceAfterQuestion();
|
|
872
1259
|
}
|
|
@@ -904,6 +1291,19 @@ export class AskDialogComponent implements Component {
|
|
|
904
1291
|
this.#requestRender();
|
|
905
1292
|
}
|
|
906
1293
|
|
|
1294
|
+
/**
|
|
1295
|
+
* Open the host prompt, image-capable when available. Returns the callback's promise so each
|
|
1296
|
+
* caller keeps one `await` before clearing `#promptActive`, which the host's restore relies on.
|
|
1297
|
+
*/
|
|
1298
|
+
#openPrompt(
|
|
1299
|
+
title: string,
|
|
1300
|
+
prefill: AskDialogPromptValue | undefined,
|
|
1301
|
+
): Promise<string | AskDialogPromptValue | undefined> {
|
|
1302
|
+
return this.callbacks.onImagePrompt
|
|
1303
|
+
? this.callbacks.onImagePrompt(title, prefill)
|
|
1304
|
+
: this.callbacks.onPrompt(title, prefill?.text);
|
|
1305
|
+
}
|
|
1306
|
+
|
|
907
1307
|
async #promptForCustomInput(
|
|
908
1308
|
question: ExtensionAskDialogQuestion,
|
|
909
1309
|
state: QuestionState,
|
|
@@ -911,18 +1311,21 @@ export class AskDialogComponent implements Component {
|
|
|
911
1311
|
): Promise<void> {
|
|
912
1312
|
this.#promptActive = true;
|
|
913
1313
|
try {
|
|
914
|
-
const
|
|
915
|
-
|
|
916
|
-
state.customInput,
|
|
917
|
-
);
|
|
918
|
-
if (
|
|
919
|
-
|
|
1314
|
+
const title = boundPromptTitle("Custom answer: ", question.question);
|
|
1315
|
+
const prefill =
|
|
1316
|
+
state.customInput === undefined ? undefined : { text: state.customInput, images: state.customInputImages };
|
|
1317
|
+
const result = await this.#openPrompt(title, prefill);
|
|
1318
|
+
if (result === undefined || this.#closed) return;
|
|
1319
|
+
const input = splitPromptInput(result);
|
|
1320
|
+
if (input.text.trim() === "") {
|
|
920
1321
|
// Submitting an empty value unselects the custom answer.
|
|
921
1322
|
state.customInput = undefined;
|
|
1323
|
+
state.customInputImages = undefined;
|
|
922
1324
|
clearNoteIfRow(state, rowItem.key);
|
|
923
1325
|
return;
|
|
924
1326
|
}
|
|
925
|
-
state.customInput = input;
|
|
1327
|
+
state.customInput = input.text;
|
|
1328
|
+
state.customInputImages = input.images;
|
|
926
1329
|
if (!question.multi) {
|
|
927
1330
|
state.selectedOptions.clear();
|
|
928
1331
|
clearNoteUnlessRow(state, rowItem.key);
|
|
@@ -947,12 +1350,15 @@ export class AskDialogComponent implements Component {
|
|
|
947
1350
|
): Promise<void> {
|
|
948
1351
|
this.#promptActive = true;
|
|
949
1352
|
try {
|
|
950
|
-
const
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
1353
|
+
const title = boundPromptTitle(`Note for ${rowItem.label}: `, question.question);
|
|
1354
|
+
const isReedit = state.noteRowKey === rowItem.key;
|
|
1355
|
+
const prefill =
|
|
1356
|
+
isReedit && state.note !== undefined ? { text: state.note, images: state.noteImages } : undefined;
|
|
1357
|
+
const result = await this.#openPrompt(title, prefill);
|
|
1358
|
+
if (result === undefined || this.#closed) return;
|
|
1359
|
+
const note = splitPromptInput(result);
|
|
1360
|
+
state.note = note.text;
|
|
1361
|
+
state.noteImages = note.images;
|
|
956
1362
|
state.noteRowKey = rowItem.key;
|
|
957
1363
|
} finally {
|
|
958
1364
|
this.#promptActive = false;
|
|
@@ -1162,6 +1568,7 @@ export class AskDialogComponent implements Component {
|
|
|
1162
1568
|
if (this.#closed) return;
|
|
1163
1569
|
this.#closed = true;
|
|
1164
1570
|
this.#countdown?.dispose();
|
|
1571
|
+
this.#stopRingTick();
|
|
1165
1572
|
this.callbacks.onSubmit({ kind: "submit", results: this.#buildResults() });
|
|
1166
1573
|
}
|
|
1167
1574
|
|
|
@@ -1169,6 +1576,7 @@ export class AskDialogComponent implements Component {
|
|
|
1169
1576
|
if (this.#closed) return;
|
|
1170
1577
|
this.#closed = true;
|
|
1171
1578
|
this.#countdown?.dispose();
|
|
1579
|
+
this.#stopRingTick();
|
|
1172
1580
|
this.callbacks.onCancel();
|
|
1173
1581
|
}
|
|
1174
1582
|
|
|
@@ -1181,6 +1589,7 @@ export class AskDialogComponent implements Component {
|
|
|
1181
1589
|
const selectedOptions = question.options
|
|
1182
1590
|
.map(option => option.label)
|
|
1183
1591
|
.filter(label => state.selectedOptions.has(label));
|
|
1592
|
+
const note = noteForSubmittedAnswer(question, state);
|
|
1184
1593
|
results.push({
|
|
1185
1594
|
id: question.id,
|
|
1186
1595
|
question: question.question,
|
|
@@ -1188,7 +1597,9 @@ export class AskDialogComponent implements Component {
|
|
|
1188
1597
|
multi: question.multi ?? false,
|
|
1189
1598
|
selectedOptions,
|
|
1190
1599
|
customInput: state.customInput,
|
|
1191
|
-
|
|
1600
|
+
customInputImages: state.customInputImages,
|
|
1601
|
+
note,
|
|
1602
|
+
noteImages: note === undefined ? undefined : state.noteImages,
|
|
1192
1603
|
timedOut: state.timedOut || undefined,
|
|
1193
1604
|
});
|
|
1194
1605
|
}
|
|
@@ -2,10 +2,14 @@ import { CancellableLoader, Container, Spacer, Text, type TUI } from "../index";
|
|
|
2
2
|
import type { Theme } from "../theme/theme";
|
|
3
3
|
import { DynamicBorder } from "../chrome/dynamic-border";
|
|
4
4
|
import { editorKey } from "../chrome/keybinding-hints";
|
|
5
|
+
import type { NativeNode } from "../native/node";
|
|
6
|
+
import { card } from "../native/describe";
|
|
7
|
+
import { actionHint, hintsRow } from "../native/overlay";
|
|
5
8
|
|
|
6
9
|
/** Loader wrapped with borders for hook UI */
|
|
7
10
|
export class BorderedLoader extends Container {
|
|
8
11
|
#loader: CancellableLoader;
|
|
12
|
+
#native: NativeNode | undefined;
|
|
9
13
|
|
|
10
14
|
constructor(tui: TUI, theme: Theme, message: string) {
|
|
11
15
|
super();
|
|
@@ -32,6 +36,19 @@ export class BorderedLoader extends Container {
|
|
|
32
36
|
this.#loader.onAbort = fn;
|
|
33
37
|
}
|
|
34
38
|
|
|
39
|
+
override invalidate(): void {
|
|
40
|
+
this.#native = undefined;
|
|
41
|
+
super.invalidate();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
override describe(): NativeNode {
|
|
45
|
+
this.#native ??= card({ role: "omp.hook.loader" }, [
|
|
46
|
+
this.#loader,
|
|
47
|
+
hintsRow([actionHint("tui.select.cancel", "cancel")]),
|
|
48
|
+
]);
|
|
49
|
+
return this.#native;
|
|
50
|
+
}
|
|
51
|
+
|
|
35
52
|
handleInput(data: string): void {
|
|
36
53
|
this.#loader.handleInput(data);
|
|
37
54
|
}
|