@oh-my-pi/pi-tui 18.4.3 → 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 +36 -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/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 +37 -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/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 +7 -0
- 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 +7 -0
- package/dist/types/setup/scenes/splash.d.ts +8 -0
- package/dist/types/setup/scenes/types.d.ts +6 -1
- 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/terminal-capabilities.d.ts +1 -1
- package/dist/types/terminal.d.ts +27 -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.d.ts +8 -0
- 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 +346 -11
- 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/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 +37 -0
- 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 +216 -27
- 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 +21 -0
- 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/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 +175 -1
- 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 +583 -23
- 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 +122 -13
- package/src/setup/scenes/splash.ts +28 -1
- package/src/setup/scenes/theme.ts +88 -3
- package/src/setup/scenes/types.ts +7 -1
- package/src/setup/startup-splash.ts +46 -9
- package/src/setup/wizard-overlay.ts +140 -23
- package/src/status-line/component.ts +398 -16
- 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/terminal-capabilities.ts +7 -0
- package/src/terminal.ts +130 -2
- 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/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/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 +180 -49
- 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 +247 -2
- 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/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.ts +113 -1
- package/src/tools/write.ts +144 -5
- package/src/tools/xdev.ts +64 -1
- package/src/tui.ts +294 -3
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import { popLoopPhase, pushLoopPhase } from "@oh-my-pi/pi-utils";
|
|
2
|
+
import { Input } from "./input";
|
|
2
3
|
import { getMenuWindow, MenuSelection } from "./menu-selection";
|
|
3
4
|
import { getKeybindings } from "../keybindings";
|
|
4
5
|
import { extractPrintableText, matchesKey } from "../keys";
|
|
5
6
|
import { type MouseRoutable, routeSelectListMouse, type SgrMouseEvent } from "../mouse";
|
|
7
|
+
import { col, node, span } from "../native/describe";
|
|
8
|
+
import { sameItems, sameProps } from "../native/memo";
|
|
9
|
+
import { plainLine } from "../native/spans";
|
|
10
|
+
import type { TspProps } from "@oh-my-pi/pi-wire";
|
|
11
|
+
import type { DescribeContext, NativeChild, NativeNode, NativeUiEvent } from "../native/node";
|
|
6
12
|
import type { SymbolTheme } from "../symbols";
|
|
7
13
|
import type { Component } from "../tui";
|
|
8
14
|
import { Ellipsis, padding, replaceTabs, truncateToWidth, visibleWidth, wrapTextWithAnsi } from "../utils";
|
|
@@ -23,12 +29,25 @@ function sanitizeSingleLine(text: string): string {
|
|
|
23
29
|
|
|
24
30
|
const clamp = (value: number, min: number, max: number): number => Math.max(min, Math.min(value, max));
|
|
25
31
|
|
|
32
|
+
/** Described `item` node cached on the item, rebuilt only when its visible fields change. */
|
|
33
|
+
const kNativeItem = Symbol("selectList.nativeItem");
|
|
34
|
+
|
|
35
|
+
interface NativeDescribedItem extends SelectItem {
|
|
36
|
+
[kNativeItem]?: { signature: string; node: NativeNode };
|
|
37
|
+
}
|
|
38
|
+
|
|
26
39
|
export interface SelectItem {
|
|
27
40
|
value: string;
|
|
28
41
|
label: string;
|
|
29
42
|
description?: string;
|
|
30
43
|
/** Optional type-indicator glyph rendered in an aligned column before the label */
|
|
31
44
|
icon?: string;
|
|
45
|
+
/** Named icon a TSP terminal draws from its own set (replaces the {@link icon} glyph natively). */
|
|
46
|
+
iconName?: string;
|
|
47
|
+
/** Native detail text when it differs from the ANSI {@link description} (which may embed {@link state}). */
|
|
48
|
+
nativeDetail?: string;
|
|
49
|
+
/** Live state drawn right-aligned natively ("demo/demo", "off"). */
|
|
50
|
+
state?: string;
|
|
32
51
|
/** Dim hint text shown inline after cursor when this item is selected */
|
|
33
52
|
hint?: string;
|
|
34
53
|
/** Disabled rows stay visible but are skipped by navigation and cannot activate. */
|
|
@@ -153,6 +172,12 @@ export class SelectList implements Component, MouseRoutable {
|
|
|
153
172
|
#hoveredIndex: number | null = null;
|
|
154
173
|
/** Per-render map of 0-based output line → filtered-item index. */
|
|
155
174
|
#hitRows: (number | undefined)[] = [];
|
|
175
|
+
#nativeList?: { props: TspProps<"list">; items: readonly NativeNode[]; node: NativeNode };
|
|
176
|
+
#nativeRoot?: { list: NativeNode; status: string; node: NativeNode };
|
|
177
|
+
/** Typed text marked at the start of native item labels (an autocomplete popup's query). */
|
|
178
|
+
#nativeMark: string | undefined;
|
|
179
|
+
/** The type-to-filter field; its value is pushed into the selection's query. */
|
|
180
|
+
readonly #search = new Input();
|
|
156
181
|
|
|
157
182
|
onSelect?: (item: SelectItem) => void;
|
|
158
183
|
onCancel?: () => void;
|
|
@@ -172,6 +197,7 @@ export class SelectList implements Component, MouseRoutable {
|
|
|
172
197
|
requiresConfirmation: item => item.confirmation !== undefined,
|
|
173
198
|
filter: layout.filterItems,
|
|
174
199
|
});
|
|
200
|
+
this.#search.prompt = "";
|
|
175
201
|
}
|
|
176
202
|
/** Return item, selection, and filter state for debug inspection. */
|
|
177
203
|
debugState(): Record<string, unknown> {
|
|
@@ -193,8 +219,10 @@ export class SelectList implements Component, MouseRoutable {
|
|
|
193
219
|
this.#maxVisible = Math.max(1, Math.trunc(rows));
|
|
194
220
|
}
|
|
195
221
|
|
|
222
|
+
/** Replace the filter text (caret to end) and refilter. */
|
|
196
223
|
setFilter(filter: string): void {
|
|
197
|
-
this.#
|
|
224
|
+
this.#search.setValue(filter);
|
|
225
|
+
this.#setFilter(this.#search.getValue(), true);
|
|
198
226
|
}
|
|
199
227
|
|
|
200
228
|
/** Replace controlled items while retaining the selected value when possible. */
|
|
@@ -215,6 +243,26 @@ export class SelectList implements Component, MouseRoutable {
|
|
|
215
243
|
return this.#selection.query;
|
|
216
244
|
}
|
|
217
245
|
|
|
246
|
+
/**
|
|
247
|
+
* What a native `picker` shows: the filtered items in order, the selected
|
|
248
|
+
* and pending values, the query and its caret (UTF-16 offset).
|
|
249
|
+
*/
|
|
250
|
+
pickerView(): {
|
|
251
|
+
readonly items: readonly SelectItem[];
|
|
252
|
+
readonly selected: string | null;
|
|
253
|
+
readonly pending: string | null;
|
|
254
|
+
readonly query: string;
|
|
255
|
+
readonly cursor: number;
|
|
256
|
+
} {
|
|
257
|
+
return {
|
|
258
|
+
items: this.#selection.visibleItems,
|
|
259
|
+
selected: this.#selection.selectedKey ?? null,
|
|
260
|
+
pending: this.#selection.pendingKey ?? null,
|
|
261
|
+
query: this.#selection.query,
|
|
262
|
+
cursor: this.#search.getCursor(),
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
|
|
218
266
|
/** Resolve a 0-based rendered-line index to a filtered-item index. */
|
|
219
267
|
hitTest(line: number): number | undefined {
|
|
220
268
|
return this.#hitRows[line];
|
|
@@ -242,6 +290,95 @@ export class SelectList implements Component, MouseRoutable {
|
|
|
242
290
|
routeSelectListMouse(this, event, line);
|
|
243
291
|
}
|
|
244
292
|
|
|
293
|
+
/**
|
|
294
|
+
* A `list` of `item`s keyed by item value, with the selection, the search
|
|
295
|
+
* query and the empty/no-match text, plus a status line (search query,
|
|
296
|
+
* pending confirmation, host status) under it when one applies.
|
|
297
|
+
*/
|
|
298
|
+
describe(_cx: DescribeContext): NativeNode {
|
|
299
|
+
const items: NativeNode[] = [];
|
|
300
|
+
for (const item of this.#selection.visibleItems) items.push(this.#describeItem(item));
|
|
301
|
+
const query = this.#selection.query;
|
|
302
|
+
const cachedMax = this.#nativeList?.props.max;
|
|
303
|
+
const props: TspProps<"list"> = {
|
|
304
|
+
selected: this.#selection.selectedKey ?? null,
|
|
305
|
+
filter: this.#nativeMark === undefined ? query.trim() || undefined : undefined,
|
|
306
|
+
empty:
|
|
307
|
+
query.trim().length > 0
|
|
308
|
+
? (this.layout.noMatchText ?? "No matching items")
|
|
309
|
+
: (this.layout.emptyText ?? "No items"),
|
|
310
|
+
max:
|
|
311
|
+
typeof cachedMax === "object" && cachedMax.lines === this.#maxVisible
|
|
312
|
+
? cachedMax
|
|
313
|
+
: { lines: this.#maxVisible },
|
|
314
|
+
};
|
|
315
|
+
const cachedList = this.#nativeList;
|
|
316
|
+
const list =
|
|
317
|
+
cachedList && sameProps(cachedList.props, props) && sameItems(cachedList.items, items)
|
|
318
|
+
? cachedList.node
|
|
319
|
+
: node("list", props, items, "list");
|
|
320
|
+
if (list !== cachedList?.node) this.#nativeList = { props, items, node: list };
|
|
321
|
+
|
|
322
|
+
const status = this.#shouldRenderSearchStatus() ? plainLine(this.#statusText()) : "";
|
|
323
|
+
const cachedRoot = this.#nativeRoot;
|
|
324
|
+
if (cachedRoot && cachedRoot.list === list && cachedRoot.status === status) return cachedRoot.node;
|
|
325
|
+
const children: NativeChild[] = [list];
|
|
326
|
+
if (status) children.push(node("text", { spans: [span(status, "muted")], wrap: "none" }, undefined, "status"));
|
|
327
|
+
const root = col(children, { role: "omp.select" });
|
|
328
|
+
this.#nativeRoot = { list, status, node: root };
|
|
329
|
+
return root;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Mark `typed` where it leads a native item label (`{s:"mark"}`), for lists
|
|
334
|
+
* whose items were matched against text outside the list (autocomplete).
|
|
335
|
+
* The list then sends no `filter`: the marks already say what matched.
|
|
336
|
+
*/
|
|
337
|
+
setNativeMark(typed: string): void {
|
|
338
|
+
this.#nativeMark = typed;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/** Pointer select/activate on an item does what a click does today: select it and request activation. */
|
|
342
|
+
handleNativeEvent(event: NativeUiEvent): void {
|
|
343
|
+
if (event.type !== "select" && event.type !== "activate") return;
|
|
344
|
+
const index = this.#selection.visibleItems.findIndex(item => item.value === event.item);
|
|
345
|
+
if (index >= 0) this.clickItem(index);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
#describeItem(item: NativeDescribedItem): NativeNode {
|
|
349
|
+
const pending = this.#selection.isPending(item);
|
|
350
|
+
const label = plainLine(this.#sanitizedLabel(item));
|
|
351
|
+
const description = plainLine((pending ? item.confirmation : this.#sanitizedDescription(item)) ?? "");
|
|
352
|
+
const detail = pending || item.nativeDetail === undefined ? description : plainLine(item.nativeDetail);
|
|
353
|
+
const icon = item.icon && item.iconName === undefined ? plainLine(item.icon) : "";
|
|
354
|
+
const state = item.state ? plainLine(item.state) : "";
|
|
355
|
+
const mark = this.#nativeMark ?? "";
|
|
356
|
+
const marked = mark.length > 0 && label.toLowerCase().startsWith(mark.toLowerCase()) ? mark.length : 0;
|
|
357
|
+
const signature =
|
|
358
|
+
`${label}\0${detail}\0${icon}\0${item.iconName}\0${state}\0${marked}\0` +
|
|
359
|
+
`${item.disabled === true}\0${pending}`;
|
|
360
|
+
const cached = item[kNativeItem];
|
|
361
|
+
if (cached?.signature === signature) return cached.node;
|
|
362
|
+
const name = marked === 0 ? [span(label)] : [span(label.slice(0, marked), "mark")];
|
|
363
|
+
if (marked > 0 && marked < label.length) name.push(span(label.slice(marked)));
|
|
364
|
+
const described = node(
|
|
365
|
+
"item",
|
|
366
|
+
{
|
|
367
|
+
// A named icon comes from the terminal's set; a bare glyph leads the label.
|
|
368
|
+
icon: item.iconName,
|
|
369
|
+
label: icon ? [span(`${icon} `, "muted"), ...name] : name,
|
|
370
|
+
detail: detail ? [span(detail, pending ? "warning" : "muted")] : undefined,
|
|
371
|
+
value: state ? [span(state, "muted")] : undefined,
|
|
372
|
+
disabled: item.disabled === true ? true : undefined,
|
|
373
|
+
tone: pending ? "warning" : undefined,
|
|
374
|
+
},
|
|
375
|
+
undefined,
|
|
376
|
+
item.value,
|
|
377
|
+
);
|
|
378
|
+
item[kNativeItem] = { signature, node: described };
|
|
379
|
+
return described;
|
|
380
|
+
}
|
|
381
|
+
|
|
245
382
|
invalidate(): void {
|
|
246
383
|
// No cached state to invalidate currently
|
|
247
384
|
}
|
|
@@ -356,14 +493,15 @@ export class SelectList implements Component, MouseRoutable {
|
|
|
356
493
|
return;
|
|
357
494
|
}
|
|
358
495
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
496
|
+
// Typed text filters even when it collides with a navigation binding; then
|
|
497
|
+
// host navigation; every other key is the search field's.
|
|
498
|
+
if (extractPrintableText(keyData) !== undefined && this.#handleSearchInput(keyData)) return;
|
|
499
|
+
const wrap = this.layout.wrapNavigation !== false;
|
|
362
500
|
let selectionChanged = false;
|
|
363
501
|
if (kb.matches(keyData, "tui.select.up")) {
|
|
364
|
-
selectionChanged = this.#selection.move(-1,
|
|
502
|
+
selectionChanged = this.#selection.move(-1, wrap);
|
|
365
503
|
} else if (kb.matches(keyData, "tui.select.down")) {
|
|
366
|
-
selectionChanged = this.#selection.move(1,
|
|
504
|
+
selectionChanged = this.#selection.move(1, wrap);
|
|
367
505
|
} else if (kb.matches(keyData, "tui.select.pageUp")) {
|
|
368
506
|
selectionChanged = this.#selection.move(-this.#maxVisible);
|
|
369
507
|
} else if (kb.matches(keyData, "tui.select.pageDown")) {
|
|
@@ -373,7 +511,9 @@ export class SelectList implements Component, MouseRoutable {
|
|
|
373
511
|
} else if (matchesKey(keyData, "end")) {
|
|
374
512
|
selectionChanged = this.#selection.moveToBoundary("last");
|
|
375
513
|
} else if (kb.matches(keyData, "tui.select.confirm") || keyData === "\n") {
|
|
376
|
-
this.#activateSelected();
|
|
514
|
+
if (this.#selection.visibleItems.length > 0) this.#activateSelected();
|
|
515
|
+
} else {
|
|
516
|
+
this.#handleSearchInput(keyData);
|
|
377
517
|
}
|
|
378
518
|
if (selectionChanged) this.#notifySelectionChange();
|
|
379
519
|
}
|
|
@@ -570,6 +710,21 @@ export class SelectList implements Component, MouseRoutable {
|
|
|
570
710
|
}
|
|
571
711
|
|
|
572
712
|
#renderStatusLine(width: number): string {
|
|
713
|
+
const avail = Math.max(1, width - 2);
|
|
714
|
+
if (
|
|
715
|
+
this.layout.statusText === undefined &&
|
|
716
|
+
this.#selection.pendingKey === undefined &&
|
|
717
|
+
this.#selection.query.length > 0
|
|
718
|
+
) {
|
|
719
|
+
const label = truncateToWidth(" Search: ", avail, Ellipsis.Omit);
|
|
720
|
+
const fieldWidth = avail - visibleWidth(label);
|
|
721
|
+
const styled = this.theme.scrollInfo(label);
|
|
722
|
+
return fieldWidth > 0 ? styled + this.#search.render(fieldWidth)[0].trimEnd() : styled;
|
|
723
|
+
}
|
|
724
|
+
return this.theme.scrollInfo(truncateToWidth(this.#statusText(), avail, Ellipsis.Omit));
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
#statusText(): string {
|
|
573
728
|
const pendingItem =
|
|
574
729
|
this.#selection.pendingKey === undefined
|
|
575
730
|
? undefined
|
|
@@ -582,12 +737,10 @@ export class SelectList implements Component, MouseRoutable {
|
|
|
582
737
|
totalCount: this.#selection.items.length,
|
|
583
738
|
pendingItem,
|
|
584
739
|
});
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
: (pendingItem?.confirmation ??
|
|
740
|
+
return this.layout.statusText !== undefined
|
|
741
|
+
? (custom ?? "")
|
|
742
|
+
: (pendingItem?.confirmation ??
|
|
589
743
|
(query ? ` Search: ${query}` : this.#canEditSearch() ? " Type to search" : ""));
|
|
590
|
-
return this.theme.scrollInfo(truncateToWidth(statusText, Math.max(1, width - 2), Ellipsis.Omit));
|
|
591
744
|
}
|
|
592
745
|
|
|
593
746
|
#shouldRenderSearchStatus(): boolean {
|
|
@@ -608,21 +761,17 @@ export class SelectList implements Component, MouseRoutable {
|
|
|
608
761
|
#handleSearchInput(keyData: string): boolean {
|
|
609
762
|
if (!this.#canEditSearch()) return false;
|
|
610
763
|
|
|
611
|
-
const
|
|
612
|
-
if (
|
|
613
|
-
if (
|
|
614
|
-
const
|
|
615
|
-
|
|
616
|
-
this.#setFilter(chars.join(""), true);
|
|
617
|
-
return true;
|
|
764
|
+
const before = this.#search.getValue();
|
|
765
|
+
if (before.length === 0) {
|
|
766
|
+
if (getKeybindings().matches(keyData, "tui.editor.deleteCharBackward")) return false;
|
|
767
|
+
const printableText = extractPrintableText(keyData);
|
|
768
|
+
if (printableText !== undefined && printableText.trim().length === 0) return false;
|
|
618
769
|
}
|
|
619
770
|
|
|
620
|
-
const
|
|
621
|
-
|
|
622
|
-
if (
|
|
623
|
-
|
|
624
|
-
this.#setFilter(this.#selection.query + printableText, true);
|
|
625
|
-
return true;
|
|
771
|
+
const consumed = this.#search.handleInput(keyData);
|
|
772
|
+
const after = this.#search.getValue();
|
|
773
|
+
if (after !== before) this.#setFilter(after, true);
|
|
774
|
+
return consumed;
|
|
626
775
|
}
|
|
627
776
|
|
|
628
777
|
#setFilter(filter: string, notify: boolean): void {
|
|
@@ -4,10 +4,15 @@ import { fuzzyFilter } from "../fuzzy";
|
|
|
4
4
|
import { getKeybindings } from "../keybindings";
|
|
5
5
|
import { extractPrintableText } from "../keys";
|
|
6
6
|
import type { MouseRoutable, SgrMouseEvent } from "../mouse";
|
|
7
|
+
import type { TspPrefsControl, TspPrefsRow, TspPrefsSection, TspProps, TspSpan } from "@oh-my-pi/pi-wire";
|
|
8
|
+
import { col, node, span } from "../native/describe";
|
|
9
|
+
import { sameItems, sameProps } from "../native/memo";
|
|
10
|
+
import { plainLine, plainText } from "../native/spans";
|
|
11
|
+
import type { DescribeContext, NativeChild, NativeNode, NativeUiEvent } from "../native/node";
|
|
7
12
|
import type { Component } from "../tui";
|
|
8
13
|
import { Ellipsis, padding, replaceTabs, truncateToWidth, visibleWidth, wrapTextWithAnsi } from "../utils";
|
|
9
14
|
import { ScrollView } from "./scroll-view";
|
|
10
|
-
import { FormField, type FormFieldOptions, type FormFieldTheme } from "./form";
|
|
15
|
+
import { FormField, type FormFieldOptions, type FormFieldTheme, SelectFormField, TextFormField } from "./form";
|
|
11
16
|
import { MenuSelection } from "./menu-selection";
|
|
12
17
|
|
|
13
18
|
function sanitizeSingleLine(text: string): string {
|
|
@@ -17,6 +22,13 @@ function sanitizeSingleLine(text: string): string {
|
|
|
17
22
|
.trim();
|
|
18
23
|
}
|
|
19
24
|
|
|
25
|
+
/** Described `item` node cached on the setting, rebuilt when a visible field (e.g. `currentValue`) changes. */
|
|
26
|
+
const kNativeItem = Symbol("settingsList.nativeItem");
|
|
27
|
+
|
|
28
|
+
interface NativeDescribedSetting extends SettingItem {
|
|
29
|
+
[kNativeItem]?: { signature: string; node: NativeNode };
|
|
30
|
+
}
|
|
31
|
+
|
|
20
32
|
function hasMouseRouter(component: Component): component is Component & MouseRoutable {
|
|
21
33
|
return "routeMouse" in component && typeof component.routeMouse === "function";
|
|
22
34
|
}
|
|
@@ -38,10 +50,59 @@ export interface SettingItem {
|
|
|
38
50
|
submenu?: (currentValue: string, done: (selectedValue?: string) => void) => Component;
|
|
39
51
|
/** True when the displayed setting differs from its default value. */
|
|
40
52
|
changed?: boolean;
|
|
53
|
+
/** The default, as a native settings page names it in the changed dot's title. */
|
|
54
|
+
defaultLabel?: string;
|
|
41
55
|
/** Render as a non-interactive section heading. Skipped by navigation and search. */
|
|
42
56
|
heading?: boolean;
|
|
43
57
|
}
|
|
44
58
|
|
|
59
|
+
/** A row's open editor as a native settings page shows it (`TspPrefsProps.editing`). */
|
|
60
|
+
export interface PrefsEditing {
|
|
61
|
+
row: string;
|
|
62
|
+
draft?: string;
|
|
63
|
+
cursor?: number;
|
|
64
|
+
option?: string;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** A submenu that reports the option it highlights (multiselect toggles) to a native settings page. */
|
|
68
|
+
export interface PrefsOptionSource {
|
|
69
|
+
readonly prefsOption: string | undefined;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** Where a setting row goes on a native settings page: its section and, for search results, its page. */
|
|
73
|
+
export interface PrefsSectionRef {
|
|
74
|
+
id: string;
|
|
75
|
+
title: string;
|
|
76
|
+
page?: string;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** The section id for a section title (`"Status Line"` → `"status-line"`). */
|
|
80
|
+
export function prefsSectionId(title: string): string {
|
|
81
|
+
return title
|
|
82
|
+
.toLowerCase()
|
|
83
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
84
|
+
.replace(/^-|-$/g, "");
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** A setting as a native settings row, given its typed control; `undefined` for headings. */
|
|
88
|
+
export function settingPrefsRow(item: SettingItem, control: TspPrefsControl): TspPrefsRow | undefined {
|
|
89
|
+
if (item.heading) return undefined;
|
|
90
|
+
const row: TspPrefsRow = {
|
|
91
|
+
id: item.id,
|
|
92
|
+
label: plainLine(item.label),
|
|
93
|
+
hint: item.description ? plainLine(item.description) : undefined,
|
|
94
|
+
warning: item.warning ? plainLine(item.warning) : undefined,
|
|
95
|
+
changed: item.changed === true ? true : undefined,
|
|
96
|
+
defaultLabel: item.changed && item.defaultLabel ? plainLine(item.defaultLabel) : undefined,
|
|
97
|
+
control,
|
|
98
|
+
};
|
|
99
|
+
return row;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function hasPrefsOption(component: Component): component is Component & PrefsOptionSource {
|
|
103
|
+
return "prefsOption" in component;
|
|
104
|
+
}
|
|
105
|
+
|
|
45
106
|
export interface SettingsListTheme {
|
|
46
107
|
label: (text: string, selected: boolean, changed: boolean) => string;
|
|
47
108
|
value: (text: string, selected: boolean, changed: boolean) => string;
|
|
@@ -133,6 +194,8 @@ export class SettingsList implements Component {
|
|
|
133
194
|
#hitRows: (string | undefined)[] = [];
|
|
134
195
|
#sidebarHitRows: (string | undefined)[] = [];
|
|
135
196
|
#sidebarHitCol = 0;
|
|
197
|
+
#nativeList?: { props: TspProps<"list">; items: readonly NativeNode[]; node: NativeNode };
|
|
198
|
+
#nativeRoot?: { body: NativeChild; hint: string; node: NativeNode };
|
|
136
199
|
constructor(
|
|
137
200
|
items: SettingItem[],
|
|
138
201
|
maxVisible: number,
|
|
@@ -222,6 +285,98 @@ export class SettingsList implements Component {
|
|
|
222
285
|
return this.#submenuComponent !== null;
|
|
223
286
|
}
|
|
224
287
|
|
|
288
|
+
/** The open submenu and the row that opened it, if any. */
|
|
289
|
+
get openSubmenu(): { id: string; component: Component } | undefined {
|
|
290
|
+
const component = this.#submenuComponent;
|
|
291
|
+
const id = this.#submenuItemId;
|
|
292
|
+
return component && id !== null ? { id, component } : undefined;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* The visible settings as native page sections: rows in list order, each
|
|
297
|
+
* in the section `sectionOf` (given the row and the heading above it) names,
|
|
298
|
+
* sections in order of their first row (ranked search results gather
|
|
299
|
+
* under their section). Rows `control` has no typed control for are left
|
|
300
|
+
* out.
|
|
301
|
+
*/
|
|
302
|
+
prefsSections(
|
|
303
|
+
sectionOf: (item: SettingItem, heading: SettingItem | undefined) => PrefsSectionRef,
|
|
304
|
+
control: (item: SettingItem) => TspPrefsControl | undefined,
|
|
305
|
+
): TspPrefsSection[] {
|
|
306
|
+
const sections: { ref: PrefsSectionRef; rows: TspPrefsRow[] }[] = [];
|
|
307
|
+
let heading: SettingItem | undefined;
|
|
308
|
+
for (const item of this.#filteredItems) {
|
|
309
|
+
if (item.heading) {
|
|
310
|
+
heading = item;
|
|
311
|
+
continue;
|
|
312
|
+
}
|
|
313
|
+
const typed = control(item);
|
|
314
|
+
const row = typed && settingPrefsRow(item, typed);
|
|
315
|
+
if (!row) continue;
|
|
316
|
+
const ref = sectionOf(item, heading);
|
|
317
|
+
const section = sections.find(candidate => candidate.ref.id === ref.id);
|
|
318
|
+
if (section) section.rows.push(row);
|
|
319
|
+
else sections.push({ ref, rows: [row] });
|
|
320
|
+
}
|
|
321
|
+
return sections.map(({ ref, rows }) => ({ ...ref, rows }));
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/** The row with keyboard focus, or while the section rail has focus the focused section's heading label. */
|
|
325
|
+
prefsFocus(): { row?: string; section?: string } {
|
|
326
|
+
if (this.#sectionFocus) {
|
|
327
|
+
const sections = this.#sections();
|
|
328
|
+
return { section: sections[this.#activeSectionIndex(sections)]?.name };
|
|
329
|
+
}
|
|
330
|
+
return { row: this.getSelectedItem()?.id };
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* The open editor as a native page draws it: a choice's highlighted option
|
|
335
|
+
* (when `inlineMenu(id)` says the row's choices fit its popup menu), a text
|
|
336
|
+
* field's draft and cursor (dots when masked), a multiselect's highlighted
|
|
337
|
+
* option; `null` when nothing is open or the editor has no native control
|
|
338
|
+
* (the host shows that one itself).
|
|
339
|
+
*/
|
|
340
|
+
prefsEditing(inlineMenu: (id: string) => boolean): PrefsEditing | null {
|
|
341
|
+
const open = this.openSubmenu;
|
|
342
|
+
if (!open) return null;
|
|
343
|
+
const { id, component } = open;
|
|
344
|
+
if (component instanceof SelectFormField) {
|
|
345
|
+
if (!inlineMenu(id)) return null;
|
|
346
|
+
return { row: id, option: component.selectList.getSelectedItem()?.value };
|
|
347
|
+
}
|
|
348
|
+
if (component instanceof TextFormField) {
|
|
349
|
+
const draft = component.getValue();
|
|
350
|
+
return {
|
|
351
|
+
row: id,
|
|
352
|
+
draft: component.input.mask ? "•".repeat(draft.length) : draft,
|
|
353
|
+
cursor: component.input.getCursor(),
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
if (hasPrefsOption(component)) return { row: id, option: component.prefsOption };
|
|
357
|
+
return null;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/** Applies `value` to a cycling (boolean/enum) row the way Enter does: the row shows it and `onChange` runs. */
|
|
361
|
+
applyValue(id: string, value: string): void {
|
|
362
|
+
const item = this.#items.find(candidate => !candidate.heading && candidate.id === id);
|
|
363
|
+
if (!item || item.submenu || !item.values?.includes(value)) return;
|
|
364
|
+
this.selectItem(id);
|
|
365
|
+
item.currentValue = value;
|
|
366
|
+
this.#onChange(item.id, value);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/** Opens row `id`'s submenu the way Enter does (leaving one already open for it); returns it. */
|
|
370
|
+
openSubmenuFor(id: string): Component | undefined {
|
|
371
|
+
const open = this.openSubmenu;
|
|
372
|
+
if (open?.id === id) return open.component;
|
|
373
|
+
if (open || !this.selectItem(id)) return undefined;
|
|
374
|
+
const item = this.getSelectedItem();
|
|
375
|
+
if (!item?.submenu) return undefined;
|
|
376
|
+
this.#activateItem();
|
|
377
|
+
return this.#submenuComponent ?? undefined;
|
|
378
|
+
}
|
|
379
|
+
|
|
225
380
|
#notifySelection(): void {
|
|
226
381
|
const item = this.getSelectedItem();
|
|
227
382
|
if (item?.id === this.#lastNotifiedSelectionId) return;
|
|
@@ -484,6 +639,113 @@ export class SettingsList implements Component {
|
|
|
484
639
|
return this.#padLines(this.#renderMainList(width));
|
|
485
640
|
}
|
|
486
641
|
|
|
642
|
+
/**
|
|
643
|
+
* A `list` of setting `item`s (label, current value on the right, risk note
|
|
644
|
+
* and description as detail; headings as disabled items) keyed by setting id,
|
|
645
|
+
* with the selection and search query, plus the footer hint. An open submenu
|
|
646
|
+
* replaces the list and describes itself.
|
|
647
|
+
*/
|
|
648
|
+
describe(_cx: DescribeContext): NativeNode {
|
|
649
|
+
let body: NativeChild;
|
|
650
|
+
let hint = "";
|
|
651
|
+
if (this.#submenuComponent) {
|
|
652
|
+
body = this.#submenuComponent;
|
|
653
|
+
} else {
|
|
654
|
+
body = this.#describeList();
|
|
655
|
+
hint = this.#options.hint === "" ? "" : plainLine(this.#hintText());
|
|
656
|
+
}
|
|
657
|
+
const cached = this.#nativeRoot;
|
|
658
|
+
if (cached && cached.body === body && cached.hint === hint) return cached.node;
|
|
659
|
+
const children: NativeChild[] = [body];
|
|
660
|
+
if (hint) children.push(node("text", { spans: [span(hint, "muted")] }, undefined, "hint"));
|
|
661
|
+
const root = col(children, { role: "omp.settings", gap: "sm" });
|
|
662
|
+
this.#nativeRoot = { body, hint, node: root };
|
|
663
|
+
return root;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
/**
|
|
667
|
+
* Pointer actions do what a click does in the settings hosts: `select` moves
|
|
668
|
+
* the selection to the row, or activates it (Enter) when it already is the
|
|
669
|
+
* selection; `activate` selects and activates.
|
|
670
|
+
*/
|
|
671
|
+
handleNativeEvent(event: NativeUiEvent): void {
|
|
672
|
+
if (this.#submenuComponent || (event.type !== "select" && event.type !== "activate")) return;
|
|
673
|
+
const wasSelected = this.getSelectedItem()?.id === event.item;
|
|
674
|
+
if (!this.selectItem(event.item)) return;
|
|
675
|
+
if (event.type === "activate" || wasSelected) this.#activateItem();
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
#describeList(): NativeNode {
|
|
679
|
+
const items: NativeNode[] = [];
|
|
680
|
+
for (const item of this.#filteredItems) items.push(this.#describeItem(item));
|
|
681
|
+
const cachedMax = this.#nativeList?.props.max;
|
|
682
|
+
const props: TspProps<"list"> = {
|
|
683
|
+
selected: this.getSelectedItem()?.id ?? null,
|
|
684
|
+
filter: this.#filterQuery.trim() || undefined,
|
|
685
|
+
empty:
|
|
686
|
+
this.#items.length === 0 ? (this.#options.emptyText ?? "No settings available") : "No matching settings",
|
|
687
|
+
max:
|
|
688
|
+
typeof cachedMax === "object" && cachedMax.lines === this.#maxVisible
|
|
689
|
+
? cachedMax
|
|
690
|
+
: { lines: this.#maxVisible },
|
|
691
|
+
};
|
|
692
|
+
const cached = this.#nativeList;
|
|
693
|
+
if (cached && sameProps(cached.props, props) && sameItems(cached.items, items)) return cached.node;
|
|
694
|
+
const list = node("list", props, items, "list");
|
|
695
|
+
this.#nativeList = { props, items, node: list };
|
|
696
|
+
return list;
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
#describeItem(item: NativeDescribedSetting): NativeNode {
|
|
700
|
+
const value = plainLine(String(item.currentValue ?? ""));
|
|
701
|
+
const changed = item.changed === true;
|
|
702
|
+
const signature = `${item.heading === true}\0${item.label}\0${value}\0${changed}\0${item.warning ?? ""}\0${item.description ?? ""}`;
|
|
703
|
+
const cached = item[kNativeItem];
|
|
704
|
+
if (cached?.signature === signature) return cached.node;
|
|
705
|
+
let described: NativeNode;
|
|
706
|
+
if (item.heading) {
|
|
707
|
+
described = node(
|
|
708
|
+
"item",
|
|
709
|
+
{ label: [span(plainLine(item.label), "strong")], disabled: true, role: "omp.settings.heading" },
|
|
710
|
+
undefined,
|
|
711
|
+
item.id,
|
|
712
|
+
);
|
|
713
|
+
} else {
|
|
714
|
+
const label: TspSpan[] = [span(plainLine(item.label), changed ? "accent" : undefined)];
|
|
715
|
+
if (item.warning && this.#theme.warningMark)
|
|
716
|
+
label.push(span(` ${plainText(this.#theme.warningMark)}`, "warning"));
|
|
717
|
+
const detail: TspSpan[] = [];
|
|
718
|
+
if (item.warning) detail.push(span(plainLine(item.warning), "warning"));
|
|
719
|
+
if (item.description) {
|
|
720
|
+
if (detail.length > 0) detail.push(span(" "));
|
|
721
|
+
detail.push(span(plainLine(item.description), "muted"));
|
|
722
|
+
}
|
|
723
|
+
described = node(
|
|
724
|
+
"item",
|
|
725
|
+
{
|
|
726
|
+
label,
|
|
727
|
+
value: [span(value, changed ? "accent" : "muted")],
|
|
728
|
+
detail: detail.length > 0 ? detail : undefined,
|
|
729
|
+
},
|
|
730
|
+
undefined,
|
|
731
|
+
item.id,
|
|
732
|
+
);
|
|
733
|
+
}
|
|
734
|
+
item[kNativeItem] = { signature, node: described };
|
|
735
|
+
return described;
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
#hintText(): string {
|
|
739
|
+
const jumpHint =
|
|
740
|
+
this.#sections().length >= 2
|
|
741
|
+
? `${editorKeys("tui.select.pageUp", "tui.select.pageDown")} to jump sections · `
|
|
742
|
+
: "";
|
|
743
|
+
return (
|
|
744
|
+
this.#options.hint ??
|
|
745
|
+
`${editorKey("tui.select.confirm")}/${formatKeyHint("space")} to change · ${jumpHint}Type to search · ${editorKey("tui.select.cancel")} to cancel`
|
|
746
|
+
);
|
|
747
|
+
}
|
|
748
|
+
|
|
487
749
|
/** Warning glyph suffix for a row that carries a risk note, or "" when none applies. */
|
|
488
750
|
#warningMark(item: SettingItem): string {
|
|
489
751
|
return item.warning && this.#theme.warningMark ? ` ${this.#theme.warningMark}` : "";
|
|
@@ -642,12 +904,7 @@ export class SettingsList implements Component {
|
|
|
642
904
|
// Add hint (suppressed entirely when the host owns the footer)
|
|
643
905
|
if (this.#options.hint !== "") {
|
|
644
906
|
lines.push("");
|
|
645
|
-
|
|
646
|
-
sections.length >= 2 ? `${editorKeys("tui.select.pageUp", "tui.select.pageDown")} to jump sections · ` : "";
|
|
647
|
-
const hintText =
|
|
648
|
-
this.#options.hint ??
|
|
649
|
-
`${editorKey("tui.select.confirm")}/${formatKeyHint("space")} to change · ${jumpHint}Type to search · ${editorKey("tui.select.cancel")} to cancel`;
|
|
650
|
-
lines.push(truncateToWidth(this.#theme.hint(` ${hintText}`), width));
|
|
907
|
+
lines.push(truncateToWidth(this.#theme.hint(` ${this.#hintText()}`), width));
|
|
651
908
|
}
|
|
652
909
|
|
|
653
910
|
return lines;
|
package/src/components/spacer.ts
CHANGED
|
@@ -1,11 +1,22 @@
|
|
|
1
|
+
import { node } from "../native/describe";
|
|
2
|
+
import type { DescribeContext, NativeNode } from "../native/node";
|
|
1
3
|
import type { Component } from "../tui";
|
|
2
4
|
|
|
5
|
+
/** Terminal spacing step for a count of blank rows. */
|
|
6
|
+
export function spaceForRows(rows: number): "none" | "sm" | "md" | "lg" {
|
|
7
|
+
if (!(rows > 0)) return "none";
|
|
8
|
+
if (rows <= 1) return "sm";
|
|
9
|
+
if (rows <= 2) return "md";
|
|
10
|
+
return "lg";
|
|
11
|
+
}
|
|
12
|
+
|
|
3
13
|
/**
|
|
4
14
|
* Spacer component that renders empty lines
|
|
5
15
|
*/
|
|
6
16
|
export class Spacer implements Component {
|
|
7
17
|
#lines: number;
|
|
8
18
|
#cached: string[] | undefined;
|
|
19
|
+
#native: NativeNode | undefined;
|
|
9
20
|
|
|
10
21
|
constructor(lines: number = 1) {
|
|
11
22
|
this.#lines = lines;
|
|
@@ -19,11 +30,17 @@ export class Spacer implements Component {
|
|
|
19
30
|
if (lines === this.#lines) return;
|
|
20
31
|
this.#lines = lines;
|
|
21
32
|
this.#cached = undefined;
|
|
33
|
+
this.#native = undefined;
|
|
22
34
|
}
|
|
23
35
|
invalidate(): void {
|
|
24
36
|
// No cached state to invalidate currently
|
|
25
37
|
}
|
|
26
38
|
|
|
39
|
+
describe(_cx: DescribeContext): NativeNode {
|
|
40
|
+
this.#native ??= node("spacer", { size: spaceForRows(this.#lines) });
|
|
41
|
+
return this.#native;
|
|
42
|
+
}
|
|
43
|
+
|
|
27
44
|
render(_width: number): readonly string[] {
|
|
28
45
|
let cached = this.#cached;
|
|
29
46
|
if (cached === undefined) {
|