@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
|
@@ -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) {
|
|
@@ -10,6 +10,9 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import { formatKeyHint } from "../app-keybindings";
|
|
12
12
|
import { matchesKey } from "../keys";
|
|
13
|
+
import { compactText, styledSpans, styleSpans } from "../native/spans";
|
|
14
|
+
import { node, row } from "../native/describe";
|
|
15
|
+
import type { DescribeContext, NativeNode, NativeUiEvent } from "../native/node";
|
|
13
16
|
import type { Component } from "../tui";
|
|
14
17
|
import { truncateToWidth, visibleWidth } from "../utils";
|
|
15
18
|
|
|
@@ -60,6 +63,7 @@ export class TabBar implements Component {
|
|
|
60
63
|
#theme: TabBarTheme;
|
|
61
64
|
#label: string;
|
|
62
65
|
#hoverTabId: string | null = null;
|
|
66
|
+
#native?: { signature: string; node: NativeNode };
|
|
63
67
|
/** Per-render tab hit zones: 0-based line + [start, end) columns. */
|
|
64
68
|
#hitZones: { line: number; start: number; end: number; index: number }[] = [];
|
|
65
69
|
|
|
@@ -161,6 +165,44 @@ export class TabBar implements Component {
|
|
|
161
165
|
// No cached state to invalidate
|
|
162
166
|
}
|
|
163
167
|
|
|
168
|
+
/**
|
|
169
|
+
* A native `tabs` strip (muted tabs keep the muted token), preceded by the
|
|
170
|
+
* bar label and followed by the cycle hint when present. Shrinking to
|
|
171
|
+
* `short` labels and wrapping are the terminal's.
|
|
172
|
+
*/
|
|
173
|
+
describe(_cx: DescribeContext): NativeNode {
|
|
174
|
+
// Tabs are a caller-owned mutable array (render keeps no cache either), so key on content.
|
|
175
|
+
let signature = `${this.#activeIndex}\0${this.#label}\0${this.showHint}`;
|
|
176
|
+
for (const tab of this.#tabs) signature += `\0${tab.id}\x01${tab.label}\x01${tab.muted === true}`;
|
|
177
|
+
if (this.#native?.signature === signature) return this.#native.node;
|
|
178
|
+
const tabs = node("tabs", {
|
|
179
|
+
items: this.#tabs.map(tab => {
|
|
180
|
+
const label = styledSpans(tab.label);
|
|
181
|
+
return {
|
|
182
|
+
id: tab.id,
|
|
183
|
+
label: tab.muted
|
|
184
|
+
? label.map(part => ({ ...part, s: part.s ? `${part.s} muted` : "muted" }))
|
|
185
|
+
: compactText(label),
|
|
186
|
+
};
|
|
187
|
+
}),
|
|
188
|
+
active: this.#tabs[this.#activeIndex]?.id,
|
|
189
|
+
});
|
|
190
|
+
const parts: NativeNode[] = [];
|
|
191
|
+
if (this.#label) parts.push(node("text", { spans: styleSpans(`${this.#label}:`, this.#theme.label) }));
|
|
192
|
+
parts.push(tabs);
|
|
193
|
+
if (this.showHint) {
|
|
194
|
+
parts.push(node("text", { spans: styleSpans(`(${formatKeyHint("tab")} to cycle)`, this.#theme.hint) }));
|
|
195
|
+
}
|
|
196
|
+
const described = parts.length === 1 ? tabs : row(parts, { gap: "md", align: "center" });
|
|
197
|
+
this.#native = { signature, node: described };
|
|
198
|
+
return described;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/** A click on a tab activates it (muted tabs stay inert). */
|
|
202
|
+
handleNativeEvent(event: NativeUiEvent): void {
|
|
203
|
+
if (event.type === "select" || event.type === "activate") this.selectTab(event.item);
|
|
204
|
+
}
|
|
205
|
+
|
|
164
206
|
/**
|
|
165
207
|
* Handle keyboard input for tab navigation.
|
|
166
208
|
* @returns true if the input was handled, false otherwise
|
package/src/components/table.ts
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import type { TspTableColumn, TspText } from "@oh-my-pi/pi-wire";
|
|
2
|
+
import { compactText, styleSpans } from "../native/spans";
|
|
3
|
+
import { node } from "../native/describe";
|
|
4
|
+
import type { DescribeContext, NativeNode } from "../native/node";
|
|
1
5
|
import type { Component } from "../tui";
|
|
2
6
|
import { Ellipsis, getWidthConfigEpoch, replaceTabs, truncateToWidth, visibleWidth } from "../utils";
|
|
3
7
|
|
|
@@ -92,6 +96,7 @@ export class Table implements Component {
|
|
|
92
96
|
readonly #columns: readonly TableColumn[];
|
|
93
97
|
readonly #options: TableOptions;
|
|
94
98
|
#cache: { width: number; epoch: number; lines: readonly string[] } | undefined;
|
|
99
|
+
#native: NativeNode | undefined;
|
|
95
100
|
|
|
96
101
|
constructor(rows: readonly (readonly TableCell[])[], columns: readonly TableColumn[], options: TableOptions = {}) {
|
|
97
102
|
this.#rows = rows;
|
|
@@ -103,11 +108,40 @@ export class Table implements Component {
|
|
|
103
108
|
if (this.#rows === rows) return false;
|
|
104
109
|
this.#rows = rows;
|
|
105
110
|
this.#cache = undefined;
|
|
111
|
+
this.#native = undefined;
|
|
106
112
|
return true;
|
|
107
113
|
}
|
|
108
114
|
|
|
109
115
|
invalidate(): void {
|
|
110
116
|
this.#cache = undefined;
|
|
117
|
+
this.#native = undefined;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* A native `table`. Column priorities carry over unchanged (lower gives
|
|
122
|
+
* way first); truncating columns truncate at the end, the rest keep
|
|
123
|
+
* their content. Widths are the terminal's.
|
|
124
|
+
*/
|
|
125
|
+
describe(_cx: DescribeContext): NativeNode {
|
|
126
|
+
if (this.#native) return this.#native;
|
|
127
|
+
const columns = this.#columns;
|
|
128
|
+
const cols: TspTableColumn[] = columns.map((column, index) => ({
|
|
129
|
+
id: `c${index}`,
|
|
130
|
+
align: column.align === "right" ? "end" : "start",
|
|
131
|
+
truncate: column.overflow === "truncate" ? "end" : undefined,
|
|
132
|
+
priority: column.priority ?? 0,
|
|
133
|
+
}));
|
|
134
|
+
const rows = this.#rows.map((row, rowIndex) => {
|
|
135
|
+
const cells: Record<string, TspText> = {};
|
|
136
|
+
const count = Math.min(row.length, columns.length);
|
|
137
|
+
for (let index = 0; index < count; index++) {
|
|
138
|
+
const cell = row[index]!;
|
|
139
|
+
cells[`c${index}`] = compactText(styleSpans(singleLine(cell.text), cell.style ?? columns[index]!.style));
|
|
140
|
+
}
|
|
141
|
+
return { id: `r${rowIndex}`, cells };
|
|
142
|
+
});
|
|
143
|
+
this.#native = node("table", { cols, rows });
|
|
144
|
+
return this.#native;
|
|
111
145
|
}
|
|
112
146
|
|
|
113
147
|
render(width: number): readonly string[] {
|
package/src/components/text.ts
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import { styledSpans } from "../native/spans";
|
|
2
|
+
import { backgroundChrome, sampleBackground } from "../native/tone";
|
|
3
|
+
import { node, text } from "../native/describe";
|
|
4
|
+
import type { DescribeContext, NativeNode } from "../native/node";
|
|
1
5
|
import type { Component } from "../tui";
|
|
2
6
|
import {
|
|
3
7
|
applyBackgroundToLine,
|
|
@@ -39,6 +43,7 @@ export class Text implements Component {
|
|
|
39
43
|
#cachedWidth?: number;
|
|
40
44
|
#cachedWidthConfigEpoch?: number;
|
|
41
45
|
#cachedLines?: string[];
|
|
46
|
+
#native?: { source: string; bg: string | undefined; node: NativeNode };
|
|
42
47
|
|
|
43
48
|
constructor(text: string = "", paddingX: number = 1, paddingY: number = 1, customBgFn?: (text: string) => string) {
|
|
44
49
|
this.#text = text;
|
|
@@ -104,6 +109,24 @@ export class Text implements Component {
|
|
|
104
109
|
this.#cachedLines = undefined;
|
|
105
110
|
}
|
|
106
111
|
|
|
112
|
+
/**
|
|
113
|
+
* Wrapped text; styling re-derived from the styled source so a theme swap
|
|
114
|
+
* re-resolves tokens. A background fill becomes the node's tone.
|
|
115
|
+
*/
|
|
116
|
+
describe(_cx: DescribeContext): NativeNode {
|
|
117
|
+
const source = this.#styleFn ? this.#styleFn(this.#text) : this.#text;
|
|
118
|
+
const bg = sampleBackground(this.#customBgFn);
|
|
119
|
+
const cached = this.#native;
|
|
120
|
+
if (cached?.source === source && cached.bg === bg) return cached.node;
|
|
121
|
+
const spans = source.trim() === "" ? [] : styledSpans(source);
|
|
122
|
+
const described =
|
|
123
|
+
spans.length === 0
|
|
124
|
+
? node("col", { hidden: true })
|
|
125
|
+
: text(spans, { wrap: "word", tone: backgroundChrome(bg).tone });
|
|
126
|
+
this.#native = { source, bg, node: described };
|
|
127
|
+
return described;
|
|
128
|
+
}
|
|
129
|
+
|
|
107
130
|
render(width: number): readonly string[] {
|
|
108
131
|
// Check cache
|
|
109
132
|
if (
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import type { TspSpan, TspTreeNode } from "@oh-my-pi/pi-wire";
|
|
2
|
+
import { styledSpans } from "../native/spans";
|
|
3
|
+
import { node } from "../native/describe";
|
|
4
|
+
import type { DescribeContext, NativeNode } from "../native/node";
|
|
1
5
|
import type { Theme } from "../theme/theme";
|
|
2
6
|
import type { Component } from "../tui";
|
|
3
7
|
import { replaceTabs, truncateToWidth, visibleWidth } from "../utils";
|
|
@@ -191,6 +195,11 @@ export interface TreeViewOptions<T, K extends TreeKey> extends FlattenTreeOption
|
|
|
191
195
|
scrollbarTheme?: ScrollViewTheme;
|
|
192
196
|
}
|
|
193
197
|
|
|
198
|
+
/** A tree node under construction; frozen into the wire shape once described. */
|
|
199
|
+
type MutableTreeNode = {
|
|
200
|
+
-readonly [P in keyof TspTreeNode]: P extends "children" ? MutableTreeNode[] : TspTreeNode[P];
|
|
201
|
+
};
|
|
202
|
+
|
|
194
203
|
/** Result of a tree render, including whether a row or line budget omitted content. */
|
|
195
204
|
export interface TreeViewRenderResult {
|
|
196
205
|
lines: readonly string[];
|
|
@@ -219,6 +228,7 @@ export class TreeView<T, K extends TreeKey = string> implements Component {
|
|
|
219
228
|
#version = 0;
|
|
220
229
|
#cache?: { width: number; version: number; result: TreeViewRenderResult };
|
|
221
230
|
#childByKey = new Map<K, Component>();
|
|
231
|
+
#native?: { version: number; node: NativeNode | null };
|
|
222
232
|
|
|
223
233
|
constructor(options: TreeViewOptions<T, K>) {
|
|
224
234
|
this.#options = options;
|
|
@@ -327,6 +337,61 @@ export class TreeView<T, K extends TreeKey = string> implements Component {
|
|
|
327
337
|
return this.renderWithState(width).lines;
|
|
328
338
|
}
|
|
329
339
|
|
|
340
|
+
/**
|
|
341
|
+
* A native disclosure `tree` over the filtered rows, nested by hierarchy
|
|
342
|
+
* (a filtered-out ancestor lifts its visible descendants to the nearest
|
|
343
|
+
* visible one). Gutters, windowing and the scrollbar are the terminal's;
|
|
344
|
+
* the selected row is marked with the `mark` token. Rows whose body is a
|
|
345
|
+
* component can't be tree labels, so such a tree stays on `rows`.
|
|
346
|
+
*/
|
|
347
|
+
describe(_cx: DescribeContext): NativeNode | null {
|
|
348
|
+
this.#ensureRows();
|
|
349
|
+
if (this.#native?.version === this.#version) return this.#native.node;
|
|
350
|
+
const rows = this.#rows;
|
|
351
|
+
const byKey = new Map<K, MutableTreeNode>();
|
|
352
|
+
const roots: MutableTreeNode[] = [];
|
|
353
|
+
let described: NativeNode | null = null;
|
|
354
|
+
let componentRow = false;
|
|
355
|
+
for (let rowIndex = 0; rowIndex < rows.length; rowIndex++) {
|
|
356
|
+
const row = rows[rowIndex]!;
|
|
357
|
+
const selected = row.key === this.#selectedKey;
|
|
358
|
+
const rendered = this.#options.renderRow(row.item, {
|
|
359
|
+
row,
|
|
360
|
+
selected,
|
|
361
|
+
rowIndex,
|
|
362
|
+
windowStart: 0,
|
|
363
|
+
windowEnd: rows.length,
|
|
364
|
+
windowRows: rows,
|
|
365
|
+
width: Number.POSITIVE_INFINITY,
|
|
366
|
+
contentWidth: Number.POSITIVE_INFINITY,
|
|
367
|
+
prefix: { first: "", continuation: "" },
|
|
368
|
+
});
|
|
369
|
+
if (typeof rendered !== "string" && "render" in rendered) {
|
|
370
|
+
this.#replaceChild(row.key, rendered);
|
|
371
|
+
componentRow = true;
|
|
372
|
+
continue;
|
|
373
|
+
}
|
|
374
|
+
this.#replaceChild(row.key, undefined);
|
|
375
|
+
let label: TspSpan[] = styledSpans(typeof rendered === "string" ? rendered : rendered.join("\n"));
|
|
376
|
+
if (selected) label = label.map(part => ({ ...part, s: part.s ? `${part.s} mark` : "mark" }));
|
|
377
|
+
const treeNode: MutableTreeNode = { id: String(row.key), label };
|
|
378
|
+
byKey.set(row.key, treeNode);
|
|
379
|
+
let parentKey = row.parentKey;
|
|
380
|
+
while (parentKey !== undefined && !byKey.has(parentKey)) parentKey = this.#rowByKey.get(parentKey)?.parentKey;
|
|
381
|
+
const parent = parentKey === undefined ? undefined : byKey.get(parentKey);
|
|
382
|
+
if (parent) {
|
|
383
|
+
parent.children ??= [];
|
|
384
|
+
parent.open = true;
|
|
385
|
+
parent.children.push(treeNode);
|
|
386
|
+
} else {
|
|
387
|
+
roots.push(treeNode);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
if (!componentRow) described = node("tree", { nodes: roots });
|
|
391
|
+
this.#native = { version: this.#version, node: described };
|
|
392
|
+
return described;
|
|
393
|
+
}
|
|
394
|
+
|
|
330
395
|
/** Render lines plus budget/window metadata for adapters that append an ellipsis. */
|
|
331
396
|
renderWithState(width: number): TreeViewRenderResult {
|
|
332
397
|
this.#ensureRows();
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { styledSpans } from "../native/spans";
|
|
2
|
+
import { text } from "../native/describe";
|
|
3
|
+
import type { DescribeContext, NativeNode } from "../native/node";
|
|
1
4
|
import type { Component } from "../tui";
|
|
2
5
|
import { padding, truncateToWidth, visibleWidth } from "../utils";
|
|
3
6
|
|
|
@@ -10,6 +13,7 @@ export class TruncatedText implements Component {
|
|
|
10
13
|
#paddingY: number;
|
|
11
14
|
#cachedWidth = -1;
|
|
12
15
|
#cachedLines: string[] | undefined;
|
|
16
|
+
#native: NativeNode | undefined;
|
|
13
17
|
|
|
14
18
|
constructor(text: string, paddingX: number = 0, paddingY: number = 0) {
|
|
15
19
|
this.#text = text;
|
|
@@ -32,10 +36,20 @@ export class TruncatedText implements Component {
|
|
|
32
36
|
}
|
|
33
37
|
|
|
34
38
|
invalidate(): void {
|
|
39
|
+
this.#native = undefined;
|
|
35
40
|
this.#cachedWidth = -1;
|
|
36
41
|
this.#cachedLines = undefined;
|
|
37
42
|
}
|
|
38
43
|
|
|
44
|
+
/** First line only, clamped to one visual line with an end ellipsis. */
|
|
45
|
+
describe(_cx: DescribeContext): NativeNode {
|
|
46
|
+
if (this.#native) return this.#native;
|
|
47
|
+
const newlineIndex = this.#text.indexOf("\n");
|
|
48
|
+
const firstLine = newlineIndex === -1 ? this.#text : this.#text.slice(0, newlineIndex);
|
|
49
|
+
this.#native = text(styledSpans(firstLine), { wrap: "none", truncate: "end", lines: 1 });
|
|
50
|
+
return this.#native;
|
|
51
|
+
}
|
|
52
|
+
|
|
39
53
|
render(width: number): readonly string[] {
|
|
40
54
|
if (this.#cachedLines && this.#cachedWidth === width) {
|
|
41
55
|
return this.#cachedLines;
|