@oh-my-pi/pi-tui 18.2.3 → 18.2.5
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 +29 -0
- package/README.md +43 -0
- package/dist/types/app-keybindings.d.ts +395 -0
- package/dist/types/apps/autoresearch-dashboard.d.ts +43 -0
- package/dist/types/apps/autoresearch-data.d.ts +15 -0
- package/dist/types/apps/cleanse-board.d.ts +66 -0
- package/dist/types/apps/cleanse-picker.d.ts +23 -0
- package/dist/types/apps/debug/log-formatting.d.ts +6 -0
- package/dist/types/apps/debug/log-viewer.d.ts +81 -0
- package/dist/types/apps/debug/protocol-probe.d.ts +42 -0
- package/dist/types/apps/debug/raw-sse-buffer.d.ts +58 -0
- package/dist/types/apps/debug/raw-sse.d.ts +26 -0
- package/dist/types/apps/debug/terminal-info.d.ts +34 -0
- package/dist/types/apps/debug/viewer-frame.d.ts +52 -0
- package/dist/types/apps/git/avatar.d.ts +9 -0
- package/dist/types/apps/git/colors.d.ts +49 -0
- package/dist/types/apps/git/diff-pane.d.ts +188 -0
- package/dist/types/apps/git/git-tui.d.ts +66 -0
- package/dist/types/apps/git/sidebar.d.ts +149 -0
- package/dist/types/apps/git/state.d.ts +117 -0
- package/dist/types/apps/if-bench-board.d.ts +75 -0
- package/dist/types/apps/live-visualizer.d.ts +33 -0
- package/dist/types/apps/ps-data.d.ts +51 -0
- package/dist/types/apps/ps-top.d.ts +33 -0
- package/dist/types/apps/session-picker.d.ts +26 -0
- package/dist/types/apps/setup-model-picker.d.ts +13 -0
- package/dist/types/apps/standalone-picker.d.ts +42 -0
- package/dist/types/autocomplete.d.ts +4 -0
- package/dist/types/chat/advisor-message.d.ts +11 -0
- package/dist/types/chat/assistant-message.d.ts +106 -0
- package/dist/types/chat/background-tan-message.d.ts +9 -0
- package/dist/types/chat/bash-execution.d.ts +47 -0
- package/dist/types/chat/cache-invalidation-marker.d.ts +50 -0
- package/dist/types/chat/chat-transcript-builder.d.ts +50 -0
- package/dist/types/chat/collab-prompt-message.d.ts +10 -0
- package/dist/types/chat/compaction-summary-message.d.ts +54 -0
- package/dist/types/chat/custom-message.d.ts +10 -0
- package/dist/types/chat/display-preferences.d.ts +13 -0
- package/dist/types/chat/eval-execution.d.ts +29 -0
- package/dist/types/chat/execution-shared.d.ts +51 -0
- package/dist/types/chat/extension-types.d.ts +28 -0
- package/dist/types/chat/hook-message.d.ts +10 -0
- package/dist/types/chat/image-loading.d.ts +46 -0
- package/dist/types/chat/late-diagnostics-message.d.ts +21 -0
- package/dist/types/chat/messages.d.ts +189 -0
- package/dist/types/chat/reaction.d.ts +30 -0
- package/dist/types/chat/read-target.d.ts +4 -0
- package/dist/types/chat/read-tool-group.d.ts +67 -0
- package/dist/types/chat/served-model-marker.d.ts +47 -0
- package/dist/types/chat/skill-message.d.ts +33 -0
- package/dist/types/chat/skill-title-input.d.ts +14 -0
- package/dist/types/chat/stripped-tool-calls-placeholder.d.ts +12 -0
- package/dist/types/chat/thinking-display.d.ts +12 -0
- package/dist/types/chat/todo-reminder.d.ts +13 -0
- package/dist/types/chat/tool-execution.d.ts +138 -0
- package/dist/types/chat/transcript-browser.d.ts +93 -0
- package/dist/types/chat/transcript-entry.d.ts +45 -0
- package/dist/types/chat/transcript-outline.d.ts +86 -0
- package/dist/types/chat/transcript-render-helpers.d.ts +92 -0
- package/dist/types/chat/ttsr-notification.d.ts +23 -0
- package/dist/types/chat/user-message.d.ts +52 -0
- package/dist/types/chrome/chat-block.d.ts +64 -0
- package/dist/types/chrome/collab-qrcode.d.ts +28 -0
- package/dist/types/chrome/context-thresholds.d.ts +10 -0
- package/dist/types/chrome/countdown-timer.d.ts +14 -0
- package/dist/types/chrome/diff.d.ts +14 -0
- package/dist/types/chrome/dynamic-border.d.ts +16 -0
- package/dist/types/chrome/error-block.d.ts +17 -0
- package/dist/types/chrome/form-theme.d.ts +3 -0
- package/dist/types/chrome/format.d.ts +12 -0
- package/dist/types/chrome/index.d.ts +20 -0
- package/dist/types/chrome/keybinding-hints.d.ts +40 -0
- package/dist/types/chrome/live-board.d.ts +23 -0
- package/dist/types/chrome/local-date.d.ts +4 -0
- package/dist/types/chrome/message-divider.d.ts +22 -0
- package/dist/types/chrome/message-frame.d.ts +52 -0
- package/dist/types/chrome/message-notice.d.ts +35 -0
- package/dist/types/chrome/overlay-box.d.ts +74 -0
- package/dist/types/chrome/qrcode.d.ts +48 -0
- package/dist/types/chrome/segment-track.d.ts +27 -0
- package/dist/types/chrome/select-list-mouse-routing.d.ts +4 -0
- package/dist/types/chrome/selector-helpers.d.ts +52 -0
- package/dist/types/chrome/shared.d.ts +5 -0
- package/dist/types/chrome/streaming-panel.d.ts +19 -0
- package/dist/types/chrome/tool-activity.d.ts +17 -0
- package/dist/types/chrome/transcript-container.d.ts +129 -0
- package/dist/types/chrome/transcript-status.d.ts +12 -0
- package/dist/types/chrome/visual-truncate.d.ts +19 -0
- package/dist/types/components/disclosure.d.ts +42 -0
- package/dist/types/components/form.d.ts +130 -0
- package/dist/types/components/image.d.ts +18 -16
- package/dist/types/components/key-value-list.d.ts +24 -0
- package/dist/types/components/layout/geometry.d.ts +73 -0
- package/dist/types/components/layout/row.d.ts +55 -0
- package/dist/types/components/layout/split-pane.d.ts +80 -0
- package/dist/types/components/layout/stack.d.ts +50 -0
- package/dist/types/components/menu-selection.d.ts +71 -0
- package/dist/types/components/metric.d.ts +44 -0
- package/dist/types/components/progress-bar.d.ts +36 -0
- package/dist/types/components/scroll-view.d.ts +53 -19
- package/dist/types/components/scroll-viewport.d.ts +46 -0
- package/dist/types/components/section.d.ts +19 -0
- package/dist/types/components/select-list.d.ts +46 -2
- package/dist/types/components/settings-list.d.ts +20 -0
- package/dist/types/components/table.d.ts +32 -0
- package/dist/types/components/tree-view.d.ts +130 -0
- package/dist/types/components/wizard-step.d.ts +60 -0
- package/dist/types/hotkeys-markdown.d.ts +7 -0
- package/dist/types/index.d.ts +17 -0
- package/dist/types/keybinding-matchers.d.ts +35 -0
- package/dist/types/keys.d.ts +2 -2
- package/dist/types/lang-from-path.d.ts +9 -0
- package/dist/types/overlays/advisor-config.d.ts +114 -0
- package/dist/types/overlays/agent-activity.d.ts +41 -0
- package/dist/types/overlays/agent-hub-projection.d.ts +33 -0
- package/dist/types/overlays/agent-hub-renderer.d.ts +53 -0
- package/dist/types/overlays/agent-hub-types.d.ts +99 -0
- package/dist/types/overlays/agent-hub.d.ts +126 -0
- package/dist/types/overlays/agent-transcript-viewer.d.ts +71 -0
- package/dist/types/overlays/agents-hub.d.ts +72 -0
- package/dist/types/overlays/ask-dialog.d.ts +88 -0
- package/dist/types/overlays/bordered-loader.d.ts +11 -0
- package/dist/types/overlays/btw-history-panel.d.ts +29 -0
- package/dist/types/overlays/btw-history.d.ts +17 -0
- package/dist/types/overlays/btw-panel.d.ts +26 -0
- package/dist/types/overlays/cleanse-panel.d.ts +36 -0
- package/dist/types/overlays/codex-reset-fireworks.d.ts +52 -0
- package/dist/types/overlays/composer-shape-preview.d.ts +47 -0
- package/dist/types/overlays/composer-shape-registry.d.ts +50 -0
- package/dist/types/overlays/copy-selector.d.ts +48 -0
- package/dist/types/overlays/copy-targets.d.ts +63 -0
- package/dist/types/overlays/error-banner.d.ts +13 -0
- package/dist/types/overlays/extensions/display-text.d.ts +10 -0
- package/dist/types/overlays/extensions/extension-dashboard.d.ts +63 -0
- package/dist/types/overlays/extensions/extension-list.d.ts +54 -0
- package/dist/types/overlays/extensions/inspector-model.d.ts +109 -0
- package/dist/types/overlays/extensions/inspector-panel.d.ts +17 -0
- package/dist/types/overlays/extensions/live-tool-session.d.ts +28 -0
- package/dist/types/overlays/extensions/mcp-runtime.d.ts +107 -0
- package/dist/types/overlays/extensions/state-manager.d.ts +42 -0
- package/dist/types/overlays/extensions/types.d.ts +165 -0
- package/dist/types/overlays/history-search.d.ts +16 -0
- package/dist/types/overlays/hook-editor.d.ts +44 -0
- package/dist/types/overlays/hook-input.d.ts +20 -0
- package/dist/types/overlays/hook-selector.d.ts +71 -0
- package/dist/types/overlays/hub-frame.d.ts +77 -0
- package/dist/types/overlays/login-dialog.d.ts +43 -0
- package/dist/types/overlays/logout-account-selector.d.ts +15 -0
- package/dist/types/overlays/mcp-add-wizard.d.ts +87 -0
- package/dist/types/overlays/model-browser.d.ts +213 -0
- package/dist/types/overlays/model-hub.d.ts +72 -0
- package/dist/types/overlays/model-picker.d.ts +74 -0
- package/dist/types/overlays/model-selector.d.ts +52 -0
- package/dist/types/overlays/move-overlay.d.ts +39 -0
- package/dist/types/overlays/oauth-selector.d.ts +41 -0
- package/dist/types/overlays/omfg-panel.d.ts +20 -0
- package/dist/types/overlays/pause-screen.d.ts +43 -0
- package/dist/types/overlays/plan-review-overlay.d.ts +89 -0
- package/dist/types/overlays/plan-save-overlay.d.ts +19 -0
- package/dist/types/overlays/plan-toc.d.ts +41 -0
- package/dist/types/overlays/plugin-selector.d.ts +28 -0
- package/dist/types/overlays/plugin-settings.d.ts +180 -0
- package/dist/types/overlays/queue-mode-selector.d.ts +11 -0
- package/dist/types/overlays/reset-usage-selector.d.ts +23 -0
- package/dist/types/overlays/rewind-selector.d.ts +61 -0
- package/dist/types/overlays/running-subagent-badge.d.ts +6 -0
- package/dist/types/overlays/session-account-selector.d.ts +24 -0
- package/dist/types/overlays/session-info-overlay.d.ts +17 -0
- package/dist/types/overlays/session-observer-registry.d.ts +77 -0
- package/dist/types/overlays/session-selector.d.ts +131 -0
- package/dist/types/overlays/settings-defs.d.ts +119 -0
- package/dist/types/overlays/settings-selector.d.ts +73 -0
- package/dist/types/overlays/show-images-selector.d.ts +11 -0
- package/dist/types/overlays/snapcompact-shape-preview.d.ts +31 -0
- package/dist/types/overlays/theme-selector.d.ts +12 -0
- package/dist/types/overlays/thinking-selector.d.ts +12 -0
- package/dist/types/overlays/tiny-title-download-progress.d.ts +21 -0
- package/dist/types/overlays/tree-selector.d.ts +92 -0
- package/dist/types/overlays/usage-dashboard.d.ts +94 -0
- package/dist/types/overlays/usage-display.d.ts +5 -0
- package/dist/types/overlays/usage-row.d.ts +17 -0
- package/dist/types/prompt/attachment-chips.d.ts +19 -0
- package/dist/types/prompt/composer-attachments.d.ts +71 -0
- package/dist/types/prompt/composer-cache.d.ts +37 -0
- package/dist/types/prompt/composer.d.ts +159 -0
- package/dist/types/prompt/custom-editor.d.ts +261 -0
- package/dist/types/prompt/editor-top-gap.d.ts +18 -0
- package/dist/types/prompt/emoji-autocomplete.d.ts +16 -0
- package/dist/types/prompt/github-ref-autocomplete.d.ts +35 -0
- package/dist/types/prompt/gradient-highlight.d.ts +32 -0
- package/dist/types/prompt/image-format.d.ts +4 -0
- package/dist/types/prompt/image-references.d.ts +26 -0
- package/dist/types/prompt/interactive-context-helpers.d.ts +43 -0
- package/dist/types/prompt/internal-url-autocomplete.d.ts +56 -0
- package/dist/types/prompt/macos-spelling.d.ts +44 -0
- package/dist/types/prompt/magic-keyword-boundary.d.ts +9 -0
- package/dist/types/prompt/magic-keywords.d.ts +26 -0
- package/dist/types/prompt/markdown-prose.d.ts +15 -0
- package/dist/types/prompt/model-mention-autocomplete.d.ts +25 -0
- package/dist/types/prompt/model-mention-syntax.d.ts +24 -0
- package/dist/types/prompt/orchestrate.d.ts +13 -0
- package/dist/types/prompt/prompt-action-autocomplete.d.ts +56 -0
- package/dist/types/prompt/queue-input.d.ts +8 -0
- package/dist/types/prompt/skill-tokens.d.ts +13 -0
- package/dist/types/prompt/tools-markdown.d.ts +12 -0
- package/dist/types/prompt/ultrathink.d.ts +13 -0
- package/dist/types/prompt/usage-amounts.d.ts +26 -0
- package/dist/types/prompt/video.d.ts +18 -0
- package/dist/types/prompt/welcome.d.ts +83 -0
- package/dist/types/prompt/workflow.d.ts +13 -0
- package/dist/types/render/code-cell.d.ts +51 -0
- package/dist/types/render/file-list.d.ts +22 -0
- package/dist/types/render/hyperlink.d.ts +80 -0
- package/dist/types/render/index.d.ts +16 -0
- package/dist/types/render/output-block.d.ts +54 -0
- package/dist/types/render/output-pane.d.ts +80 -0
- package/dist/types/render/render-utils.d.ts +312 -0
- package/dist/types/render/sixel.d.ts +21 -0
- package/dist/types/render/status-line.d.ts +23 -0
- package/dist/types/render/tool-card.d.ts +71 -0
- package/dist/types/render/tree-list.d.ts +29 -0
- package/dist/types/render/types.d.ts +17 -0
- package/dist/types/render/utils.d.ts +43 -0
- package/dist/types/render/width-aware-text.d.ts +23 -0
- package/dist/types/setup/lazy.d.ts +3 -0
- package/dist/types/setup/scenes/composer.d.ts +3 -0
- package/dist/types/setup/scenes/glyph.d.ts +3 -0
- package/dist/types/setup/scenes/model.d.ts +3 -0
- package/dist/types/setup/scenes/outro.d.ts +2 -0
- package/dist/types/setup/scenes/providers.d.ts +3 -0
- package/dist/types/setup/scenes/sign-in.d.ts +21 -0
- package/dist/types/setup/scenes/splash.d.ts +11 -0
- package/dist/types/setup/scenes/theme.d.ts +3 -0
- package/dist/types/setup/scenes/types.d.ts +110 -0
- package/dist/types/setup/scenes/web-search.d.ts +22 -0
- package/dist/types/setup/setup-version.d.ts +11 -0
- package/dist/types/setup/startup-splash.d.ts +9 -0
- package/dist/types/setup/wizard-overlay.d.ts +16 -0
- package/dist/types/setup/wizard.d.ts +25 -0
- package/dist/types/status-line/component.d.ts +182 -0
- package/dist/types/status-line/context-usage.d.ts +156 -0
- package/dist/types/status-line/footer.d.ts +30 -0
- package/dist/types/status-line/git-utils.d.ts +22 -0
- package/dist/types/status-line/host.d.ts +129 -0
- package/dist/types/status-line/index.d.ts +6 -0
- package/dist/types/status-line/loop.d.ts +18 -0
- package/dist/types/status-line/metrics.d.ts +21 -0
- package/dist/types/status-line/presets.d.ts +3 -0
- package/dist/types/status-line/schema.d.ts +15 -0
- package/dist/types/status-line/segments.d.ts +5 -0
- package/dist/types/status-line/separators.d.ts +3 -0
- package/dist/types/status-line/types.d.ts +236 -0
- package/dist/types/stdin-buffer.d.ts +4 -0
- package/dist/types/theme/color.d.ts +24 -0
- package/dist/types/theme/defaults/index.d.ts +9489 -0
- package/dist/types/theme/index.d.ts +4 -0
- package/dist/types/theme/loader.d.ts +23 -0
- package/dist/types/theme/mermaid-cache.d.ts +26 -0
- package/dist/types/theme/schema-validation.d.ts +3 -0
- package/dist/types/theme/schema.d.ts +25 -0
- package/dist/types/theme/session-color.d.ts +43 -0
- package/dist/types/theme/shimmer.d.ts +44 -0
- package/dist/types/theme/symbols.d.ts +34 -0
- package/dist/types/theme/theme-class.d.ts +299 -0
- package/dist/types/theme/theme.d.ts +111 -0
- package/dist/types/theme/tui-adapters.d.ts +30 -0
- package/dist/types/thinking.d.ts +165 -0
- package/dist/types/tools/agent-tree.d.ts +36 -0
- package/dist/types/tools/ask.d.ts +67 -0
- package/dist/types/tools/ast-edit.d.ts +53 -0
- package/dist/types/tools/ast-grep.d.ts +52 -0
- package/dist/types/tools/autoresearch.d.ts +156 -0
- package/dist/types/tools/bash-interactive.d.ts +28 -0
- package/dist/types/tools/bash.d.ts +103 -0
- package/dist/types/tools/conflict-detect.d.ts +43 -0
- package/dist/types/tools/debug.d.ts +60 -0
- package/dist/types/tools/default-renderer.d.ts +24 -0
- package/dist/types/tools/edit.d.ts +158 -0
- package/dist/types/tools/eval-format/code-mode-declarations.d.ts +5 -0
- package/dist/types/tools/eval-format/index.d.ts +5 -0
- package/dist/types/tools/eval-format/javascript.d.ts +2 -0
- package/dist/types/tools/eval-format/python.d.ts +2 -0
- package/dist/types/tools/eval.d.ts +94 -0
- package/dist/types/tools/fetch.d.ts +42 -0
- package/dist/types/tools/gh-format.d.ts +8 -0
- package/dist/types/tools/github.d.ts +144 -0
- package/dist/types/tools/glob.d.ts +44 -0
- package/dist/types/tools/goal.d.ts +42 -0
- package/dist/types/tools/grep.d.ts +64 -0
- package/dist/types/tools/grouped-file-output.d.ts +71 -0
- package/dist/types/tools/hashline-format.d.ts +28 -0
- package/dist/types/tools/hub.d.ts +340 -0
- package/dist/types/tools/index.d.ts +4 -0
- package/dist/types/tools/json-tree.d.ts +59 -0
- package/dist/types/tools/line-ranges.d.ts +29 -0
- package/dist/types/tools/list-limit.d.ts +15 -0
- package/dist/types/tools/lsp.d.ts +72 -0
- package/dist/types/tools/match-line-format.d.ts +11 -0
- package/dist/types/tools/mcp.d.ts +81 -0
- package/dist/types/tools/memory.d.ts +67 -0
- package/dist/types/tools/output-meta.d.ts +151 -0
- package/dist/types/tools/read.d.ts +139 -0
- package/dist/types/tools/renderer.d.ts +98 -0
- package/dist/types/tools/report-tool-issue.d.ts +8 -0
- package/dist/types/tools/resolve.d.ts +47 -0
- package/dist/types/tools/streaming-output.d.ts +282 -0
- package/dist/types/tools/subprocess.d.ts +24 -0
- package/dist/types/tools/task-repair-args.d.ts +53 -0
- package/dist/types/tools/task-yield-assembly.d.ts +20 -0
- package/dist/types/tools/task.d.ts +425 -0
- package/dist/types/tools/terminal-output.d.ts +8 -0
- package/dist/types/tools/think.d.ts +13 -0
- package/dist/types/tools/todo.d.ts +118 -0
- package/dist/types/tools/tool-errors.d.ts +7 -0
- package/dist/types/tools/vibe.d.ts +120 -0
- package/dist/types/tools/web-search.d.ts +195 -0
- package/dist/types/tools/write.d.ts +76 -0
- package/dist/types/tools/xd-url.d.ts +13 -0
- package/dist/types/tools/xdev.d.ts +52 -0
- package/dist/types/tui.d.ts +18 -0
- package/dist/types/utils.d.ts +2 -0
- package/package.json +37 -3
- package/src/app-keybindings.ts +760 -0
- package/src/apps/autoresearch-dashboard.ts +500 -0
- package/src/apps/autoresearch-data.ts +64 -0
- package/src/apps/cleanse-board.ts +341 -0
- package/src/apps/cleanse-picker.ts +57 -0
- package/src/apps/debug/log-formatting.ts +53 -0
- package/src/apps/debug/log-viewer.ts +868 -0
- package/src/apps/debug/protocol-probe.ts +267 -0
- package/src/apps/debug/raw-sse-buffer.ts +449 -0
- package/src/apps/debug/raw-sse.ts +282 -0
- package/src/apps/debug/terminal-info.ts +115 -0
- package/src/apps/debug/viewer-frame.ts +174 -0
- package/src/apps/git/avatar.ts +52 -0
- package/src/apps/git/colors.ts +109 -0
- package/src/apps/git/diff-pane.ts +1512 -0
- package/src/apps/git/git-tui.ts +889 -0
- package/src/apps/git/sidebar.ts +1204 -0
- package/src/apps/git/state.ts +113 -0
- package/src/apps/if-bench-board.ts +324 -0
- package/src/apps/live-visualizer.ts +238 -0
- package/src/apps/ps-data.ts +116 -0
- package/src/apps/ps-top.ts +422 -0
- package/src/apps/session-picker.ts +95 -0
- package/src/apps/setup-model-picker.ts +21 -0
- package/src/apps/standalone-picker.ts +111 -0
- package/src/autocomplete.ts +9 -13
- package/src/chat/advisor-message.ts +175 -0
- package/src/chat/assistant-message.ts +1214 -0
- package/src/chat/background-tan-message.ts +35 -0
- package/src/chat/bash-execution.ts +394 -0
- package/src/chat/cache-invalidation-marker.ts +94 -0
- package/src/chat/chat-transcript-builder.ts +593 -0
- package/src/chat/collab-prompt-message.ts +34 -0
- package/src/chat/compaction-summary-message.ts +261 -0
- package/src/chat/custom-message.ts +23 -0
- package/src/chat/display-preferences.ts +24 -0
- package/src/chat/eval-execution.ts +172 -0
- package/src/chat/execution-shared.ts +113 -0
- package/src/chat/extension-types.ts +46 -0
- package/src/chat/hook-message.ts +20 -0
- package/src/chat/image-loading.ts +122 -0
- package/src/chat/late-diagnostics-message.ts +92 -0
- package/src/chat/messages.ts +305 -0
- package/src/chat/reaction.ts +65 -0
- package/src/chat/read-target.ts +7 -0
- package/src/chat/read-tool-group.ts +873 -0
- package/src/chat/served-model-marker.ts +91 -0
- package/src/chat/skill-message.ts +220 -0
- package/src/chat/skill-title-input.ts +41 -0
- package/src/chat/stripped-tool-calls-placeholder.ts +35 -0
- package/src/chat/thinking-display.ts +312 -0
- package/src/chat/todo-reminder.ts +33 -0
- package/src/chat/tool-execution.ts +1395 -0
- package/src/chat/transcript-browser.ts +268 -0
- package/src/chat/transcript-entry.ts +96 -0
- package/src/chat/transcript-outline.ts +260 -0
- package/src/chat/transcript-render-helpers.ts +315 -0
- package/src/chat/ttsr-notification.ts +123 -0
- package/src/chat/user-message.ts +287 -0
- package/src/chrome/chat-block.ts +110 -0
- package/src/chrome/collab-qrcode.ts +74 -0
- package/src/chrome/context-thresholds.ts +85 -0
- package/src/chrome/countdown-timer.ts +74 -0
- package/src/chrome/diff.ts +266 -0
- package/src/chrome/dynamic-border.ts +36 -0
- package/src/chrome/error-block.ts +63 -0
- package/src/chrome/form-theme.ts +10 -0
- package/src/chrome/format.ts +66 -0
- package/src/chrome/index.ts +21 -0
- package/src/chrome/keybinding-hints.ts +55 -0
- package/src/chrome/live-board.ts +123 -0
- package/src/chrome/local-date.ts +20 -0
- package/src/chrome/message-divider.ts +51 -0
- package/src/chrome/message-frame.ts +164 -0
- package/src/chrome/message-notice.ts +103 -0
- package/src/chrome/overlay-box.ts +301 -0
- package/src/chrome/qrcode.ts +539 -0
- package/src/chrome/segment-track.ts +86 -0
- package/src/chrome/select-list-mouse-routing.ts +11 -0
- package/src/chrome/selector-helpers.ts +119 -0
- package/src/chrome/shared.ts +27 -0
- package/src/chrome/streaming-panel.ts +72 -0
- package/src/chrome/tool-activity.ts +44 -0
- package/src/chrome/transcript-container.ts +902 -0
- package/src/chrome/transcript-status.ts +27 -0
- package/src/chrome/visual-truncate.ts +70 -0
- package/src/components/disclosure.ts +235 -0
- package/src/components/editor.ts +10 -12
- package/src/components/form.ts +566 -0
- package/src/components/image.ts +80 -53
- package/src/components/input.ts +6 -27
- package/src/components/key-value-list.ts +88 -0
- package/src/components/layout/geometry.ts +202 -0
- package/src/components/layout/row.ts +283 -0
- package/src/components/layout/split-pane.ts +349 -0
- package/src/components/layout/stack.ts +261 -0
- package/src/components/markdown.ts +16 -12
- package/src/components/menu-selection.ts +248 -0
- package/src/components/metric.ts +132 -0
- package/src/components/progress-bar.ts +140 -0
- package/src/components/scroll-view.ts +372 -70
- package/src/components/scroll-viewport.ts +154 -0
- package/src/components/section.ts +77 -0
- package/src/components/select-list.ts +238 -159
- package/src/components/settings-list.ts +103 -80
- package/src/components/table.ts +127 -0
- package/src/components/tree-view.ts +481 -0
- package/src/components/wizard-step.ts +229 -0
- package/src/deccara.ts +1 -3
- package/src/hotkeys-markdown.ts +100 -0
- package/src/index.ts +18 -0
- package/src/keybinding-matchers.ts +99 -0
- package/src/keys.ts +2 -2
- package/src/lang-from-path.ts +252 -0
- package/src/overlays/advisor-config.ts +761 -0
- package/src/overlays/agent-activity.ts +113 -0
- package/src/overlays/agent-hub-projection.ts +250 -0
- package/src/overlays/agent-hub-renderer.ts +281 -0
- package/src/overlays/agent-hub-types.ts +74 -0
- package/src/overlays/agent-hub.ts +1562 -0
- package/src/overlays/agent-transcript-viewer.ts +669 -0
- package/src/overlays/agents-hub.ts +1203 -0
- package/src/overlays/ask-dialog.ts +1202 -0
- package/src/overlays/bordered-loader.ts +41 -0
- package/src/overlays/btw-history-panel.ts +591 -0
- package/src/overlays/btw-history.ts +33 -0
- package/src/overlays/btw-panel.ts +167 -0
- package/src/overlays/cleanse-panel.ts +167 -0
- package/src/overlays/codex-reset-fireworks.ts +375 -0
- package/src/overlays/composer-shape-preview.ts +137 -0
- package/src/overlays/composer-shape-registry.ts +82 -0
- package/src/overlays/copy-selector.ts +618 -0
- package/src/overlays/copy-targets.ts +210 -0
- package/src/overlays/error-banner.ts +39 -0
- package/src/overlays/extensions/display-text.ts +39 -0
- package/src/overlays/extensions/extension-dashboard.ts +645 -0
- package/src/overlays/extensions/extension-list.ts +667 -0
- package/src/overlays/extensions/inspector-model.ts +484 -0
- package/src/overlays/extensions/inspector-panel.ts +669 -0
- package/src/overlays/extensions/live-tool-session.ts +67 -0
- package/src/overlays/extensions/mcp-runtime.ts +289 -0
- package/src/overlays/extensions/state-manager.ts +337 -0
- package/src/overlays/extensions/types.ts +208 -0
- package/src/overlays/history-search.ts +268 -0
- package/src/overlays/hook-editor.ts +272 -0
- package/src/overlays/hook-input.ts +84 -0
- package/src/overlays/hook-selector.ts +635 -0
- package/src/overlays/hub-frame.ts +262 -0
- package/src/overlays/login-dialog.ts +231 -0
- package/src/overlays/logout-account-selector.ts +115 -0
- package/src/overlays/mcp-add-wizard.ts +1436 -0
- package/src/overlays/model-browser.ts +1176 -0
- package/src/overlays/model-hub.ts +2279 -0
- package/src/overlays/model-picker.ts +285 -0
- package/src/overlays/model-selector.ts +107 -0
- package/src/overlays/move-overlay.ts +197 -0
- package/src/overlays/oauth-selector.ts +446 -0
- package/src/overlays/omfg-panel.ts +136 -0
- package/src/overlays/pause-screen.ts +198 -0
- package/src/overlays/plan-review-overlay.ts +1227 -0
- package/src/overlays/plan-save-overlay.ts +74 -0
- package/src/overlays/plan-toc.ts +139 -0
- package/src/overlays/plugin-selector.ts +97 -0
- package/src/overlays/plugin-settings.ts +839 -0
- package/src/overlays/queue-mode-selector.ts +55 -0
- package/src/overlays/reset-usage-selector.ts +170 -0
- package/src/overlays/rewind-selector.ts +499 -0
- package/src/overlays/running-subagent-badge.ts +19 -0
- package/src/overlays/session-account-selector.ts +68 -0
- package/src/overlays/session-info-overlay.ts +96 -0
- package/src/overlays/session-observer-registry.ts +289 -0
- package/src/overlays/session-selector.ts +1137 -0
- package/src/overlays/settings-defs.ts +315 -0
- package/src/overlays/settings-selector.ts +1415 -0
- package/src/overlays/show-images-selector.ts +44 -0
- package/src/overlays/snapcompact-shape-preview-doc.md +14 -0
- package/src/overlays/snapcompact-shape-preview.ts +192 -0
- package/src/overlays/theme-selector.ts +62 -0
- package/src/overlays/thinking-selector.ts +51 -0
- package/src/overlays/tiny-title-download-progress.ts +102 -0
- package/src/overlays/tree-selector.ts +1121 -0
- package/src/overlays/usage-dashboard.ts +629 -0
- package/src/overlays/usage-display.ts +45 -0
- package/src/overlays/usage-row.ts +118 -0
- package/src/prompt/attachment-chips.ts +201 -0
- package/src/prompt/composer-attachments.ts +280 -0
- package/src/prompt/composer-cache.ts +304 -0
- package/src/prompt/composer.ts +934 -0
- package/src/prompt/custom-editor.ts +1392 -0
- package/src/prompt/data/emojis.json +1 -0
- package/src/prompt/editor-top-gap.ts +28 -0
- package/src/prompt/emoji-autocomplete.ts +285 -0
- package/src/prompt/github-ref-autocomplete.ts +75 -0
- package/src/prompt/gradient-highlight.ts +101 -0
- package/src/prompt/image-format.ts +28 -0
- package/src/prompt/image-references.ts +102 -0
- package/src/prompt/interactive-context-helpers.ts +126 -0
- package/src/prompt/internal-url-autocomplete.ts +155 -0
- package/src/prompt/macos-spelling.ts +456 -0
- package/src/prompt/magic-keyword-boundary.ts +23 -0
- package/src/prompt/magic-keywords.ts +42 -0
- package/src/prompt/markdown-prose.ts +245 -0
- package/src/prompt/model-mention-autocomplete.ts +94 -0
- package/src/prompt/model-mention-syntax.ts +41 -0
- package/src/prompt/orchestrate.ts +26 -0
- package/src/prompt/prompt-action-autocomplete.ts +337 -0
- package/src/prompt/queue-input.ts +132 -0
- package/src/prompt/skill-tokens.ts +36 -0
- package/src/prompt/tips.txt +27 -0
- package/src/prompt/tools-markdown.ts +29 -0
- package/src/prompt/ultrathink.ts +26 -0
- package/src/prompt/usage-amounts.ts +99 -0
- package/src/prompt/video.ts +47 -0
- package/src/prompt/welcome.ts +605 -0
- package/src/prompt/workflow.ts +26 -0
- package/src/render/code-cell.ts +244 -0
- package/src/render/file-list.ts +58 -0
- package/src/render/hyperlink.ts +192 -0
- package/src/render/index.ts +17 -0
- package/src/render/output-block.ts +267 -0
- package/src/render/output-pane.ts +299 -0
- package/src/render/render-utils.ts +1182 -0
- package/src/render/sixel.ts +69 -0
- package/src/render/status-line.ts +55 -0
- package/src/render/tool-card.ts +272 -0
- package/src/render/tree-list.ts +188 -0
- package/src/render/types.ts +19 -0
- package/src/render/utils.ts +109 -0
- package/src/render/width-aware-text.ts +60 -0
- package/src/setup/lazy.ts +17 -0
- package/src/setup/scenes/composer.ts +127 -0
- package/src/setup/scenes/glyph.ts +120 -0
- package/src/setup/scenes/model.ts +138 -0
- package/src/setup/scenes/outro.ts +24 -0
- package/src/setup/scenes/providers.ts +107 -0
- package/src/setup/scenes/sign-in.ts +373 -0
- package/src/setup/scenes/splash.ts +192 -0
- package/src/setup/scenes/theme.ts +343 -0
- package/src/setup/scenes/types.ts +111 -0
- package/src/setup/scenes/web-search.ts +165 -0
- package/src/setup/setup-version.ts +11 -0
- package/src/setup/startup-splash.ts +107 -0
- package/src/setup/wizard-overlay.ts +319 -0
- package/src/setup/wizard.ts +101 -0
- package/src/status-line/component.ts +3003 -0
- package/src/status-line/context-usage.ts +688 -0
- package/src/status-line/footer.ts +323 -0
- package/src/status-line/git-utils.ts +42 -0
- package/src/status-line/host.ts +97 -0
- package/src/status-line/index.ts +6 -0
- package/src/status-line/loop.ts +28 -0
- package/src/status-line/metrics.ts +94 -0
- package/src/status-line/presets.ts +107 -0
- package/src/status-line/schema.ts +57 -0
- package/src/status-line/segments.ts +934 -0
- package/src/status-line/separators.ts +55 -0
- package/src/status-line/types.ts +227 -0
- package/src/stdin-buffer.ts +4 -2
- package/src/theme/color.ts +95 -0
- package/src/theme/dark.json +95 -0
- package/src/theme/defaults/alabaster.json +93 -0
- package/src/theme/defaults/amethyst.json +96 -0
- package/src/theme/defaults/anthracite.json +93 -0
- package/src/theme/defaults/basalt.json +91 -0
- package/src/theme/defaults/birch.json +95 -0
- package/src/theme/defaults/dark-abyss.json +91 -0
- package/src/theme/defaults/dark-arctic.json +104 -0
- package/src/theme/defaults/dark-aurora.json +95 -0
- package/src/theme/defaults/dark-catppuccin.json +107 -0
- package/src/theme/defaults/dark-cavern.json +91 -0
- package/src/theme/defaults/dark-celestial.json +106 -0
- package/src/theme/defaults/dark-copper.json +95 -0
- package/src/theme/defaults/dark-cosmos.json +90 -0
- package/src/theme/defaults/dark-cyberpunk.json +102 -0
- package/src/theme/defaults/dark-dracula.json +98 -0
- package/src/theme/defaults/dark-eclipse.json +91 -0
- package/src/theme/defaults/dark-ember.json +95 -0
- package/src/theme/defaults/dark-equinox.json +90 -0
- package/src/theme/defaults/dark-forest.json +96 -0
- package/src/theme/defaults/dark-github.json +105 -0
- package/src/theme/defaults/dark-gruvbox.json +112 -0
- package/src/theme/defaults/dark-lavender.json +95 -0
- package/src/theme/defaults/dark-lunar.json +89 -0
- package/src/theme/defaults/dark-midnight.json +95 -0
- package/src/theme/defaults/dark-monochrome.json +94 -0
- package/src/theme/defaults/dark-monokai.json +98 -0
- package/src/theme/defaults/dark-nebula.json +90 -0
- package/src/theme/defaults/dark-nord.json +98 -0
- package/src/theme/defaults/dark-ocean.json +101 -0
- package/src/theme/defaults/dark-one.json +100 -0
- package/src/theme/defaults/dark-poimandres.json +115 -0
- package/src/theme/defaults/dark-rainforest.json +91 -0
- package/src/theme/defaults/dark-reef.json +91 -0
- package/src/theme/defaults/dark-retro.json +92 -0
- package/src/theme/defaults/dark-rose-pine.json +96 -0
- package/src/theme/defaults/dark-sakura.json +95 -0
- package/src/theme/defaults/dark-slate.json +95 -0
- package/src/theme/defaults/dark-solarized.json +97 -0
- package/src/theme/defaults/dark-solstice.json +90 -0
- package/src/theme/defaults/dark-starfall.json +91 -0
- package/src/theme/defaults/dark-sunset.json +99 -0
- package/src/theme/defaults/dark-swamp.json +90 -0
- package/src/theme/defaults/dark-synthwave.json +103 -0
- package/src/theme/defaults/dark-taiga.json +91 -0
- package/src/theme/defaults/dark-terminal.json +95 -0
- package/src/theme/defaults/dark-tokyo-night.json +101 -0
- package/src/theme/defaults/dark-tundra.json +91 -0
- package/src/theme/defaults/dark-twilight.json +91 -0
- package/src/theme/defaults/dark-volcanic.json +91 -0
- package/src/theme/defaults/graphite.json +92 -0
- package/src/theme/defaults/index.ts +201 -0
- package/src/theme/defaults/light-arctic.json +107 -0
- package/src/theme/defaults/light-aurora-day.json +91 -0
- package/src/theme/defaults/light-canyon.json +91 -0
- package/src/theme/defaults/light-catppuccin.json +106 -0
- package/src/theme/defaults/light-cirrus.json +90 -0
- package/src/theme/defaults/light-coral.json +95 -0
- package/src/theme/defaults/light-cyberpunk.json +96 -0
- package/src/theme/defaults/light-dawn.json +90 -0
- package/src/theme/defaults/light-dunes.json +91 -0
- package/src/theme/defaults/light-eucalyptus.json +95 -0
- package/src/theme/defaults/light-forest.json +100 -0
- package/src/theme/defaults/light-frost.json +95 -0
- package/src/theme/defaults/light-github.json +115 -0
- package/src/theme/defaults/light-glacier.json +91 -0
- package/src/theme/defaults/light-gruvbox.json +108 -0
- package/src/theme/defaults/light-haze.json +90 -0
- package/src/theme/defaults/light-honeycomb.json +95 -0
- package/src/theme/defaults/light-lagoon.json +91 -0
- package/src/theme/defaults/light-lavender.json +95 -0
- package/src/theme/defaults/light-meadow.json +91 -0
- package/src/theme/defaults/light-mint.json +95 -0
- package/src/theme/defaults/light-monochrome.json +101 -0
- package/src/theme/defaults/light-ocean.json +99 -0
- package/src/theme/defaults/light-one.json +99 -0
- package/src/theme/defaults/light-opal.json +91 -0
- package/src/theme/defaults/light-orchard.json +91 -0
- package/src/theme/defaults/light-paper.json +95 -0
- package/src/theme/defaults/light-poimandres.json +115 -0
- package/src/theme/defaults/light-prism.json +90 -0
- package/src/theme/defaults/light-retro.json +98 -0
- package/src/theme/defaults/light-sand.json +95 -0
- package/src/theme/defaults/light-savanna.json +91 -0
- package/src/theme/defaults/light-solarized.json +102 -0
- package/src/theme/defaults/light-soleil.json +90 -0
- package/src/theme/defaults/light-sunset.json +99 -0
- package/src/theme/defaults/light-synthwave.json +98 -0
- package/src/theme/defaults/light-tokyo-night.json +111 -0
- package/src/theme/defaults/light-wetland.json +91 -0
- package/src/theme/defaults/light-zenith.json +89 -0
- package/src/theme/defaults/limestone.json +94 -0
- package/src/theme/defaults/mahogany.json +97 -0
- package/src/theme/defaults/marble.json +93 -0
- package/src/theme/defaults/obsidian.json +91 -0
- package/src/theme/defaults/onyx.json +91 -0
- package/src/theme/defaults/pearl.json +93 -0
- package/src/theme/defaults/porcelain.json +91 -0
- package/src/theme/defaults/quartz.json +96 -0
- package/src/theme/defaults/sandstone.json +95 -0
- package/src/theme/defaults/titanium.json +90 -0
- package/src/theme/index.ts +5 -0
- package/src/theme/light.json +93 -0
- package/src/theme/loader.ts +200 -0
- package/src/theme/mermaid-cache.ts +92 -0
- package/src/theme/schema-validation.ts +119 -0
- package/src/theme/schema.ts +168 -0
- package/src/theme/session-color.ts +253 -0
- package/src/theme/shimmer.ts +307 -0
- package/src/theme/symbols.ts +1433 -0
- package/src/theme/theme-class.ts +787 -0
- package/src/theme/theme-schema.json +463 -0
- package/src/theme/theme.ts +810 -0
- package/src/theme/tui-adapters.ts +339 -0
- package/src/thinking.ts +380 -0
- package/src/tools/agent-tree.ts +128 -0
- package/src/tools/ask.ts +482 -0
- package/src/tools/ast-edit.ts +210 -0
- package/src/tools/ast-grep.ts +171 -0
- package/src/tools/autoresearch.ts +287 -0
- package/src/tools/bash-interactive.ts +295 -0
- package/src/tools/bash.ts +468 -0
- package/src/tools/conflict-detect.ts +77 -0
- package/src/tools/debug.ts +167 -0
- package/src/tools/default-renderer.ts +172 -0
- package/src/tools/edit.ts +1258 -0
- package/src/tools/eval-format/code-mode-declarations.ts +61 -0
- package/src/tools/eval-format/index.ts +16 -0
- package/src/tools/eval-format/javascript.ts +952 -0
- package/src/tools/eval-format/python.ts +544 -0
- package/src/tools/eval.ts +835 -0
- package/src/tools/fetch.ts +226 -0
- package/src/tools/gh-format.ts +18 -0
- package/src/tools/github.ts +686 -0
- package/src/tools/glob.ts +213 -0
- package/src/tools/goal.ts +153 -0
- package/src/tools/grep.ts +385 -0
- package/src/tools/grouped-file-output.ts +211 -0
- package/src/tools/hashline-format.ts +59 -0
- package/src/tools/hub.ts +1488 -0
- package/src/tools/index.ts +71 -0
- package/src/tools/json-tree.ts +388 -0
- package/src/tools/line-ranges.ts +98 -0
- package/src/tools/list-limit.ts +43 -0
- package/src/tools/lsp.ts +702 -0
- package/src/tools/match-line-format.ts +20 -0
- package/src/tools/mcp.ts +309 -0
- package/src/tools/memory.ts +220 -0
- package/src/tools/output-meta.ts +367 -0
- package/src/tools/read.ts +628 -0
- package/src/tools/renderer.ts +107 -0
- package/src/tools/report-tool-issue.ts +13 -0
- package/src/tools/resolve.ts +139 -0
- package/src/tools/streaming-output.ts +1576 -0
- package/src/tools/subprocess.ts +39 -0
- package/src/tools/task-repair-args.ts +118 -0
- package/src/tools/task-yield-assembly.ts +147 -0
- package/src/tools/task.ts +2110 -0
- package/src/tools/terminal-output.ts +153 -0
- package/src/tools/think.ts +28 -0
- package/src/tools/todo.ts +580 -0
- package/src/tools/tool-errors.ts +15 -0
- package/src/tools/vibe.ts +420 -0
- package/src/tools/web-search.ts +409 -0
- package/src/tools/write.ts +511 -0
- package/src/tools/xd-url.ts +28 -0
- package/src/tools/xdev.ts +201 -0
- package/src/tui.ts +70 -8
- package/src/utils.ts +8 -0
|
@@ -0,0 +1,481 @@
|
|
|
1
|
+
import type { Theme } from "../theme/theme";
|
|
2
|
+
import type { Component } from "../tui";
|
|
3
|
+
import { replaceTabs, truncateToWidth, visibleWidth } from "../utils";
|
|
4
|
+
import { ScrollView, type ScrollViewTheme } from "./scroll-view";
|
|
5
|
+
import { centeredViewportRange, viewportRange } from "./scroll-viewport";
|
|
6
|
+
|
|
7
|
+
/** Stable identity accepted by {@link TreeView}. Keys must be unique within one hierarchy. */
|
|
8
|
+
export type TreeKey = string | number;
|
|
9
|
+
|
|
10
|
+
/** One ancestor retained on a flattened hierarchy row. */
|
|
11
|
+
export interface TreeAncestor<K extends TreeKey> {
|
|
12
|
+
key: K;
|
|
13
|
+
depth: number;
|
|
14
|
+
isLast: boolean;
|
|
15
|
+
siblingCount: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Structural metadata for one item in depth-first display order. */
|
|
19
|
+
export interface TreeRow<T, K extends TreeKey> {
|
|
20
|
+
item: T;
|
|
21
|
+
key: K;
|
|
22
|
+
parentKey: K | undefined;
|
|
23
|
+
depth: number;
|
|
24
|
+
index: number;
|
|
25
|
+
siblingIndex: number;
|
|
26
|
+
siblingCount: number;
|
|
27
|
+
isLast: boolean;
|
|
28
|
+
ancestors: readonly TreeAncestor<K>[];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Controls the iterative hierarchy projection shared by display and interactive trees. */
|
|
32
|
+
export interface FlattenTreeOptions<T, K extends TreeKey> {
|
|
33
|
+
roots: readonly T[];
|
|
34
|
+
getKey: (item: T) => K;
|
|
35
|
+
getChildren: (item: T, row: TreeRow<T, K>) => readonly T[];
|
|
36
|
+
/** False omits descendants while retaining the item itself. Defaults to true. */
|
|
37
|
+
isExpanded?: (item: T, row: TreeRow<T, K>) => boolean;
|
|
38
|
+
/** Display depth assigned to roots. Defaults to zero. */
|
|
39
|
+
rootDepth?: number;
|
|
40
|
+
/** Override the display depth inherited by direct children. */
|
|
41
|
+
getChildDepth?: (item: T, row: TreeRow<T, K>, children: readonly T[]) => number;
|
|
42
|
+
/** Stop projection after this many structural items. Omit for no traversal cap. */
|
|
43
|
+
maxItems?: number;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
interface PendingTreeRow<T, K extends TreeKey> {
|
|
47
|
+
item: T;
|
|
48
|
+
parentKey: K | undefined;
|
|
49
|
+
depth: number;
|
|
50
|
+
siblingIndex: number;
|
|
51
|
+
siblingCount: number;
|
|
52
|
+
ancestors: readonly TreeAncestor<K>[];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
interface FlattenTreeResult<T, K extends TreeKey> {
|
|
56
|
+
rows: readonly TreeRow<T, K>[];
|
|
57
|
+
truncated: boolean;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function projectTree<T, K extends TreeKey>(options: FlattenTreeOptions<T, K>): FlattenTreeResult<T, K> {
|
|
61
|
+
const rows: TreeRow<T, K>[] = [];
|
|
62
|
+
const roots = options.roots;
|
|
63
|
+
const rootDepth = Math.max(0, Math.trunc(options.rootDepth ?? 0));
|
|
64
|
+
const maxItems =
|
|
65
|
+
options.maxItems === undefined || !Number.isFinite(options.maxItems)
|
|
66
|
+
? Number.POSITIVE_INFINITY
|
|
67
|
+
: Math.max(0, Math.trunc(options.maxItems));
|
|
68
|
+
const stack: PendingTreeRow<T, K>[] = [];
|
|
69
|
+
for (let index = roots.length - 1; index >= 0; index--) {
|
|
70
|
+
stack.push({
|
|
71
|
+
item: roots[index],
|
|
72
|
+
parentKey: undefined,
|
|
73
|
+
depth: rootDepth,
|
|
74
|
+
siblingIndex: index,
|
|
75
|
+
siblingCount: roots.length,
|
|
76
|
+
ancestors: [],
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
while (stack.length > 0 && rows.length < maxItems) {
|
|
81
|
+
const pending = stack.pop()!;
|
|
82
|
+
const key = options.getKey(pending.item);
|
|
83
|
+
const row: TreeRow<T, K> = {
|
|
84
|
+
item: pending.item,
|
|
85
|
+
key,
|
|
86
|
+
parentKey: pending.parentKey,
|
|
87
|
+
depth: pending.depth,
|
|
88
|
+
index: rows.length,
|
|
89
|
+
siblingIndex: pending.siblingIndex,
|
|
90
|
+
siblingCount: pending.siblingCount,
|
|
91
|
+
isLast: pending.siblingIndex === pending.siblingCount - 1,
|
|
92
|
+
ancestors: pending.ancestors,
|
|
93
|
+
};
|
|
94
|
+
rows.push(row);
|
|
95
|
+
|
|
96
|
+
if (options.isExpanded && !options.isExpanded(pending.item, row)) continue;
|
|
97
|
+
const children = options.getChildren(pending.item, row);
|
|
98
|
+
if (children.length === 0) continue;
|
|
99
|
+
const childDepth = Math.max(
|
|
100
|
+
0,
|
|
101
|
+
Math.trunc(options.getChildDepth?.(pending.item, row, children) ?? pending.depth + 1),
|
|
102
|
+
);
|
|
103
|
+
const ancestors: readonly TreeAncestor<K>[] = [
|
|
104
|
+
...pending.ancestors,
|
|
105
|
+
{ key, depth: pending.depth, isLast: row.isLast, siblingCount: pending.siblingCount },
|
|
106
|
+
];
|
|
107
|
+
for (let index = children.length - 1; index >= 0; index--) {
|
|
108
|
+
stack.push({
|
|
109
|
+
item: children[index],
|
|
110
|
+
parentKey: key,
|
|
111
|
+
depth: childDepth,
|
|
112
|
+
siblingIndex: index,
|
|
113
|
+
siblingCount: children.length,
|
|
114
|
+
ancestors,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return { rows, truncated: stack.length > 0 };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** First-line and continuation prefixes for a rendered tree item. */
|
|
122
|
+
export interface TreePrefix {
|
|
123
|
+
first: string;
|
|
124
|
+
continuation: string;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** Optional symbol styling for {@link treeRowPrefix}. */
|
|
128
|
+
export interface TreePrefixStyle {
|
|
129
|
+
connector?: (symbol: string) => string;
|
|
130
|
+
vertical?: (symbol: string) => string;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** Render the conventional three-cell tree gutter for a flattened row. */
|
|
134
|
+
export function treeRowPrefix<T, K extends TreeKey>(
|
|
135
|
+
row: TreeRow<T, K>,
|
|
136
|
+
theme: Theme,
|
|
137
|
+
style?: TreePrefixStyle,
|
|
138
|
+
): TreePrefix {
|
|
139
|
+
const vertical = style?.vertical ? style.vertical(theme.tree.vertical) : theme.fg("dim", theme.tree.vertical);
|
|
140
|
+
let ancestors = "";
|
|
141
|
+
for (const ancestor of row.ancestors) {
|
|
142
|
+
ancestors += ancestor.isLast ? " " : `${vertical} `;
|
|
143
|
+
}
|
|
144
|
+
const connectorSymbol = row.isLast ? theme.tree.last : theme.tree.branch;
|
|
145
|
+
const connector = style?.connector ? style.connector(connectorSymbol) : theme.fg("dim", connectorSymbol);
|
|
146
|
+
const continuation = row.isLast ? " " : `${vertical} `;
|
|
147
|
+
return { first: `${ancestors}${connector} `, continuation: `${ancestors}${continuation}` };
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/** Context supplied while laying out one visible tree row. */
|
|
151
|
+
export interface TreeViewRowContext<T, K extends TreeKey> {
|
|
152
|
+
row: TreeRow<T, K>;
|
|
153
|
+
selected: boolean;
|
|
154
|
+
rowIndex: number;
|
|
155
|
+
windowStart: number;
|
|
156
|
+
windowEnd: number;
|
|
157
|
+
windowRows: readonly TreeRow<T, K>[];
|
|
158
|
+
width: number;
|
|
159
|
+
contentWidth: number;
|
|
160
|
+
prefix: TreePrefix;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/** Row bodies may be plain lines or an existing component rendered at the remaining width. */
|
|
164
|
+
export type TreeViewRowContent = string | readonly string[] | Component;
|
|
165
|
+
|
|
166
|
+
/** Rendering, filtering, selection, expansion, and budget policy for {@link TreeView}. */
|
|
167
|
+
export interface TreeViewOptions<T, K extends TreeKey> extends FlattenTreeOptions<T, K> {
|
|
168
|
+
theme: Theme;
|
|
169
|
+
renderRow: (item: T, context: TreeViewRowContext<T, K>) => TreeViewRowContent;
|
|
170
|
+
filter?: (item: T, row: TreeRow<T, K>) => boolean;
|
|
171
|
+
selectedKey?: K;
|
|
172
|
+
/** Logical rows shown around the selection. Omit to render every filtered row. */
|
|
173
|
+
maxRows?: number;
|
|
174
|
+
/** Strict physical-line budget after multiline row expansion. */
|
|
175
|
+
maxLines?: number;
|
|
176
|
+
/** Defaults to true when `selectedKey` is provided. */
|
|
177
|
+
centerSelection?: boolean;
|
|
178
|
+
/** Custom indentation/gutter geometry. */
|
|
179
|
+
renderPrefix?: (
|
|
180
|
+
row: TreeRow<T, K>,
|
|
181
|
+
context: Omit<TreeViewRowContext<T, K>, "prefix" | "contentWidth" | "selected">,
|
|
182
|
+
) => TreePrefix;
|
|
183
|
+
/** Content before the tree gutter, such as an interactive selection cursor. */
|
|
184
|
+
renderLeading?: (item: T, context: TreeViewRowContext<T, K>) => string;
|
|
185
|
+
/** Applied to every physical line belonging to the selected logical row. */
|
|
186
|
+
styleSelected?: (line: string) => string;
|
|
187
|
+
/** Bound physical rows to `render(width)`. Defaults to true; disable only for formatting adapters. */
|
|
188
|
+
truncateRows?: boolean;
|
|
189
|
+
/** Add a viewport scrollbar when `maxRows` hides logical rows. */
|
|
190
|
+
scrollbar?: boolean;
|
|
191
|
+
scrollbarTheme?: ScrollViewTheme;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/** Result of a tree render, including whether a row or line budget omitted content. */
|
|
195
|
+
export interface TreeViewRenderResult {
|
|
196
|
+
lines: readonly string[];
|
|
197
|
+
truncated: boolean;
|
|
198
|
+
windowStart: number;
|
|
199
|
+
windowEnd: number;
|
|
200
|
+
totalRows: number;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Reusable keyed hierarchy component. It keeps flattened and rendered arrays
|
|
205
|
+
* stable by reference until its inputs, selection, filter, or width change.
|
|
206
|
+
*/
|
|
207
|
+
export class TreeView<T, K extends TreeKey = string> implements Component {
|
|
208
|
+
readonly #options: TreeViewOptions<T, K>;
|
|
209
|
+
#filter: ((item: T, row: TreeRow<T, K>) => boolean) | undefined;
|
|
210
|
+
#selectedKey: K | undefined;
|
|
211
|
+
#selectionAnchor: K | undefined;
|
|
212
|
+
#allRows: readonly TreeRow<T, K>[] = [];
|
|
213
|
+
#rows: readonly TreeRow<T, K>[] = [];
|
|
214
|
+
#rowByKey = new Map<K, TreeRow<T, K>>();
|
|
215
|
+
#visibleIndexByKey = new Map<K, number>();
|
|
216
|
+
#projectionTruncated = false;
|
|
217
|
+
#structureDirty = true;
|
|
218
|
+
#filterDirty = true;
|
|
219
|
+
#version = 0;
|
|
220
|
+
#cache?: { width: number; version: number; result: TreeViewRenderResult };
|
|
221
|
+
#childByKey = new Map<K, Component>();
|
|
222
|
+
|
|
223
|
+
constructor(options: TreeViewOptions<T, K>) {
|
|
224
|
+
this.#options = options;
|
|
225
|
+
this.#filter = options.filter;
|
|
226
|
+
this.#selectedKey = options.selectedKey;
|
|
227
|
+
this.#selectionAnchor = options.selectedKey;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/** Rebuild hierarchy state on the next access and discard rendered lines. */
|
|
231
|
+
invalidate(): void {
|
|
232
|
+
this.#structureDirty = true;
|
|
233
|
+
this.#filterDirty = true;
|
|
234
|
+
for (const child of new Set(this.#childByKey.values())) child.invalidate?.();
|
|
235
|
+
this.#touch();
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
dispose(): void {
|
|
239
|
+
for (const child of new Set(this.#childByKey.values())) child.dispose?.();
|
|
240
|
+
this.#childByKey.clear();
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/** Replace the visible-row predicate while retaining the nearest possible selection. */
|
|
244
|
+
setFilter(filter: ((item: T, row: TreeRow<T, K>) => boolean) | undefined): void {
|
|
245
|
+
this.#filter = filter;
|
|
246
|
+
this.#filterDirty = true;
|
|
247
|
+
this.#touch();
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/** Every structural row before filtering. The returned array is immutable and stable until invalidated. */
|
|
251
|
+
get allRows(): readonly TreeRow<T, K>[] {
|
|
252
|
+
this.#ensureRows();
|
|
253
|
+
return this.#allRows;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/** Filtered rows in navigation order. The returned array is immutable and stable until invalidated. */
|
|
257
|
+
get rows(): readonly TreeRow<T, K>[] {
|
|
258
|
+
this.#ensureRows();
|
|
259
|
+
return this.#rows;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/** Currently selected key, or undefined for an empty tree. */
|
|
263
|
+
get selectedKey(): K | undefined {
|
|
264
|
+
this.#ensureRows();
|
|
265
|
+
return this.#selectedKey;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/** Currently selected item, or undefined for an empty tree. */
|
|
269
|
+
get selectedItem(): T | undefined {
|
|
270
|
+
const index = this.selectionIndex;
|
|
271
|
+
return index < 0 ? undefined : this.#rows[index]?.item;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/** Index in filtered navigation order, or -1 when empty. */
|
|
275
|
+
get selectionIndex(): number {
|
|
276
|
+
this.#ensureRows();
|
|
277
|
+
if (this.#selectedKey === undefined) return -1;
|
|
278
|
+
return this.#visibleIndexByKey.get(this.#selectedKey) ?? -1;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/** Select a key, walking to its nearest visible ancestor when filtered. */
|
|
282
|
+
setSelectedKey(key: K | undefined): void {
|
|
283
|
+
this.#ensureRows();
|
|
284
|
+
const resolved = this.#nearestVisibleKey(key);
|
|
285
|
+
if (this.#selectedKey === resolved && (resolved === undefined || this.#selectionAnchor === resolved)) return;
|
|
286
|
+
this.#selectedKey = resolved;
|
|
287
|
+
if (resolved !== undefined) this.#selectionAnchor = resolved;
|
|
288
|
+
else if (key !== undefined) this.#selectionAnchor = key;
|
|
289
|
+
this.#touch();
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/** Select a filtered-row index, clamped to the current range. */
|
|
293
|
+
setSelectionIndex(index: number): void {
|
|
294
|
+
this.#ensureRows();
|
|
295
|
+
if (this.#rows.length === 0) {
|
|
296
|
+
this.setSelectedKey(undefined);
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
const clamped = Math.max(0, Math.min(Math.trunc(index), this.#rows.length - 1));
|
|
300
|
+
this.setSelectedKey(this.#rows[clamped]?.key);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/** Move by logical rows. Wrapping is opt-in for menu-style interaction. */
|
|
304
|
+
moveSelection(delta: number, wrap = false): void {
|
|
305
|
+
this.#ensureRows();
|
|
306
|
+
const count = this.#rows.length;
|
|
307
|
+
if (count === 0) return;
|
|
308
|
+
const current = Math.max(0, this.selectionIndex);
|
|
309
|
+
let next = current + Math.trunc(delta);
|
|
310
|
+
if (wrap) next = ((next % count) + count) % count;
|
|
311
|
+
this.setSelectionIndex(Math.max(0, Math.min(next, count - 1)));
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/** Move to the next row in `direction` satisfying a domain predicate. */
|
|
315
|
+
moveSelectionWhere(predicate: (item: T, row: TreeRow<T, K>) => boolean, direction: -1 | 1): void {
|
|
316
|
+
this.#ensureRows();
|
|
317
|
+
for (let index = this.selectionIndex + direction; index >= 0 && index < this.#rows.length; index += direction) {
|
|
318
|
+
const row = this.#rows[index]!;
|
|
319
|
+
if (predicate(row.item, row)) {
|
|
320
|
+
this.setSelectedKey(row.key);
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
render(width: number): readonly string[] {
|
|
327
|
+
return this.renderWithState(width).lines;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/** Render lines plus budget/window metadata for adapters that append an ellipsis. */
|
|
331
|
+
renderWithState(width: number): TreeViewRenderResult {
|
|
332
|
+
this.#ensureRows();
|
|
333
|
+
const safeWidth = Number.isFinite(width) ? Math.max(0, Math.trunc(width)) : Number.POSITIVE_INFINITY;
|
|
334
|
+
if (this.#cache?.width === safeWidth && this.#cache.version === this.#version) return this.#cache.result;
|
|
335
|
+
|
|
336
|
+
const totalRows = this.#rows.length;
|
|
337
|
+
const maxRows = this.#normalizeBudget(this.#options.maxRows, totalRows);
|
|
338
|
+
const selectedIndex = this.selectionIndex;
|
|
339
|
+
const centerSelection = this.#options.centerSelection ?? this.#selectedKey !== undefined;
|
|
340
|
+
const window =
|
|
341
|
+
centerSelection && selectedIndex >= 0
|
|
342
|
+
? centeredViewportRange(selectedIndex, totalRows, maxRows)
|
|
343
|
+
: viewportRange(totalRows, maxRows, 0);
|
|
344
|
+
const windowStart = window.start;
|
|
345
|
+
const windowEnd = window.end;
|
|
346
|
+
const windowRows = this.#rows.slice(windowStart, windowEnd);
|
|
347
|
+
const maxLines = this.#normalizeBudget(this.#options.maxLines, Number.POSITIVE_INFINITY);
|
|
348
|
+
const lines: string[] = [];
|
|
349
|
+
let truncated = this.#projectionTruncated || windowStart > 0 || windowEnd < totalRows;
|
|
350
|
+
|
|
351
|
+
for (let localIndex = 0; localIndex < windowRows.length; localIndex++) {
|
|
352
|
+
const row = windowRows[localIndex]!;
|
|
353
|
+
const rowIndex = windowStart + localIndex;
|
|
354
|
+
const baseContext = {
|
|
355
|
+
row,
|
|
356
|
+
rowIndex,
|
|
357
|
+
windowStart,
|
|
358
|
+
windowEnd,
|
|
359
|
+
windowRows,
|
|
360
|
+
width: safeWidth,
|
|
361
|
+
};
|
|
362
|
+
const prefix = this.#options.renderPrefix?.(row, baseContext) ?? treeRowPrefix(row, this.#options.theme);
|
|
363
|
+
const selected = row.key === this.#selectedKey;
|
|
364
|
+
const provisionalContext: TreeViewRowContext<T, K> = {
|
|
365
|
+
...baseContext,
|
|
366
|
+
selected,
|
|
367
|
+
contentWidth: safeWidth,
|
|
368
|
+
prefix,
|
|
369
|
+
};
|
|
370
|
+
const leading = this.#options.renderLeading?.(row.item, provisionalContext) ?? "";
|
|
371
|
+
const contentWidth = Number.isFinite(safeWidth)
|
|
372
|
+
? Math.max(0, safeWidth - visibleWidth(leading) - visibleWidth(prefix.first))
|
|
373
|
+
: safeWidth;
|
|
374
|
+
const context: TreeViewRowContext<T, K> = { ...baseContext, selected, contentWidth, prefix };
|
|
375
|
+
const rendered = this.#options.renderRow(row.item, context);
|
|
376
|
+
const linesBeforeRow = lines.length;
|
|
377
|
+
let body: readonly string[];
|
|
378
|
+
if (typeof rendered === "string") {
|
|
379
|
+
this.#replaceChild(row.key, undefined);
|
|
380
|
+
body = rendered.length > 0 ? [rendered] : [];
|
|
381
|
+
} else if ("render" in rendered) {
|
|
382
|
+
this.#replaceChild(row.key, rendered);
|
|
383
|
+
body = rendered.render(contentWidth);
|
|
384
|
+
} else {
|
|
385
|
+
this.#replaceChild(row.key, undefined);
|
|
386
|
+
body = rendered;
|
|
387
|
+
}
|
|
388
|
+
if (body.length === 0) continue;
|
|
389
|
+
|
|
390
|
+
for (let lineIndex = 0; lineIndex < body.length; lineIndex++) {
|
|
391
|
+
if (lines.length >= maxLines) {
|
|
392
|
+
truncated = true;
|
|
393
|
+
break;
|
|
394
|
+
}
|
|
395
|
+
const gutter = lineIndex === 0 ? prefix.first : prefix.continuation;
|
|
396
|
+
let line = `${leading}${gutter}${replaceTabs(body[lineIndex] ?? "")}`;
|
|
397
|
+
if (selected && this.#options.styleSelected) line = this.#options.styleSelected(line);
|
|
398
|
+
if (this.#options.truncateRows !== false && Number.isFinite(safeWidth))
|
|
399
|
+
line = truncateToWidth(line, safeWidth);
|
|
400
|
+
lines.push(line);
|
|
401
|
+
}
|
|
402
|
+
if (lines.length >= maxLines) {
|
|
403
|
+
if (body.length > lines.length - linesBeforeRow || localIndex < windowRows.length - 1) truncated = true;
|
|
404
|
+
break;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
let renderedLines: readonly string[] = lines;
|
|
409
|
+
if (this.#options.scrollbar && maxRows < totalRows && lines.length > 0 && Number.isFinite(safeWidth)) {
|
|
410
|
+
const viewport = new ScrollView(lines, {
|
|
411
|
+
height: lines.length,
|
|
412
|
+
totalRows,
|
|
413
|
+
scrollbar: "auto",
|
|
414
|
+
theme: this.#options.scrollbarTheme,
|
|
415
|
+
});
|
|
416
|
+
viewport.setScrollOffset(windowStart);
|
|
417
|
+
renderedLines = viewport.render(safeWidth);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
const result: TreeViewRenderResult = { lines: renderedLines, truncated, windowStart, windowEnd, totalRows };
|
|
421
|
+
this.#cache = { width: safeWidth, version: this.#version, result };
|
|
422
|
+
return result;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
#ensureRows(): void {
|
|
426
|
+
if (this.#structureDirty) {
|
|
427
|
+
const projection = projectTree(this.#options);
|
|
428
|
+
this.#allRows = projection.rows;
|
|
429
|
+
this.#projectionTruncated = projection.truncated;
|
|
430
|
+
this.#rowByKey = new Map<K, TreeRow<T, K>>();
|
|
431
|
+
for (const row of this.#allRows) this.#rowByKey.set(row.key, row);
|
|
432
|
+
for (const key of this.#childByKey.keys()) {
|
|
433
|
+
if (!this.#rowByKey.has(key)) this.#replaceChild(key, undefined);
|
|
434
|
+
}
|
|
435
|
+
this.#structureDirty = false;
|
|
436
|
+
this.#filterDirty = true;
|
|
437
|
+
}
|
|
438
|
+
if (!this.#filterDirty) return;
|
|
439
|
+
this.#rows = this.#filter ? this.#allRows.filter(row => this.#filter!(row.item, row)) : this.#allRows;
|
|
440
|
+
this.#visibleIndexByKey = new Map<K, number>();
|
|
441
|
+
for (let index = 0; index < this.#rows.length; index++) {
|
|
442
|
+
this.#visibleIndexByKey.set(this.#rows[index]!.key, index);
|
|
443
|
+
}
|
|
444
|
+
this.#selectedKey = this.#nearestVisibleKey(this.#selectionAnchor);
|
|
445
|
+
if (this.#selectedKey !== undefined) this.#selectionAnchor = this.#selectedKey;
|
|
446
|
+
this.#filterDirty = false;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
#nearestVisibleKey(key: K | undefined): K | undefined {
|
|
450
|
+
if (this.#rows.length === 0) return undefined;
|
|
451
|
+
if (key === undefined) return this.#rows[0]?.key;
|
|
452
|
+
let candidate: K | undefined = key;
|
|
453
|
+
while (candidate !== undefined) {
|
|
454
|
+
if (this.#visibleIndexByKey.has(candidate)) return candidate;
|
|
455
|
+
candidate = this.#rowByKey.get(candidate)?.parentKey;
|
|
456
|
+
}
|
|
457
|
+
return this.#rows[this.#rows.length - 1]?.key;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
#replaceChild(key: K, child: Component | undefined): void {
|
|
461
|
+
const previous = this.#childByKey.get(key);
|
|
462
|
+
if (previous === child) return;
|
|
463
|
+
if (child) this.#childByKey.set(key, child);
|
|
464
|
+
else this.#childByKey.delete(key);
|
|
465
|
+
if (!previous) return;
|
|
466
|
+
for (const retained of this.#childByKey.values()) {
|
|
467
|
+
if (retained === previous) return;
|
|
468
|
+
}
|
|
469
|
+
previous.dispose?.();
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
#normalizeBudget(value: number | undefined, fallback: number): number {
|
|
473
|
+
if (value === undefined || !Number.isFinite(value)) return fallback;
|
|
474
|
+
return Math.max(0, Math.trunc(value));
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
#touch(): void {
|
|
478
|
+
this.#version++;
|
|
479
|
+
this.#cache = undefined;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import type { MouseRoutable, SgrMouseEvent } from "../mouse";
|
|
2
|
+
import type { Component } from "../tui";
|
|
3
|
+
|
|
4
|
+
/** Semantic role of a wizard step's primary interactive content. */
|
|
5
|
+
export type WizardStepKind = "input" | "choice" | "confirm" | "async" | "custom";
|
|
6
|
+
|
|
7
|
+
/** A component slot rendered by {@link WizardStep}. */
|
|
8
|
+
export interface WizardStepSlot {
|
|
9
|
+
readonly component: Component;
|
|
10
|
+
/** Omit this slot when retaining it would squeeze content below `minContentLines`. */
|
|
11
|
+
readonly optional?: boolean;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** Composition and height policy for a single wizard step. */
|
|
15
|
+
export interface WizardStepOptions {
|
|
16
|
+
readonly kind: WizardStepKind;
|
|
17
|
+
readonly content: Component;
|
|
18
|
+
readonly heading?: Component;
|
|
19
|
+
readonly intro?: Component;
|
|
20
|
+
readonly preview?: WizardStepSlot;
|
|
21
|
+
readonly status?: Component;
|
|
22
|
+
readonly footer?: Component;
|
|
23
|
+
/** Blank rows inserted between non-empty slots. Defaults to one. */
|
|
24
|
+
readonly gap?: number;
|
|
25
|
+
/** Minimum useful content height used when deciding whether to show an optional preview. */
|
|
26
|
+
readonly minContentLines?: number;
|
|
27
|
+
/** Resize content-owned viewports before their next render. */
|
|
28
|
+
readonly fitContent?: (maxLines: number | undefined) => void;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
interface RenderedSlot {
|
|
32
|
+
readonly component: Component;
|
|
33
|
+
readonly lines: readonly string[];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function isMouseRoutable(component: Component): component is Component & MouseRoutable {
|
|
37
|
+
return "routeMouse" in component && typeof component.routeMouse === "function";
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function sameLines(left: readonly string[], right: readonly string[]): boolean {
|
|
41
|
+
if (left.length !== right.length) return false;
|
|
42
|
+
for (let i = 0; i < left.length; i++) {
|
|
43
|
+
if (left[i] !== right[i]) return false;
|
|
44
|
+
}
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Reusable presentation for input, choice, confirmation, and asynchronous
|
|
50
|
+
* wizard steps. Controllers retain state transitions; this component owns the
|
|
51
|
+
* ordered slots, bounded content budget, optional-preview collapse, and mouse
|
|
52
|
+
* coordinate translation into the interactive content child.
|
|
53
|
+
*/
|
|
54
|
+
export class WizardStep implements Component, MouseRoutable {
|
|
55
|
+
readonly debugKind = "WizardStep";
|
|
56
|
+
|
|
57
|
+
#kind: WizardStepKind;
|
|
58
|
+
#content: Component;
|
|
59
|
+
#heading: Component | undefined;
|
|
60
|
+
#intro: Component | undefined;
|
|
61
|
+
#preview: WizardStepSlot | undefined;
|
|
62
|
+
#status: Component | undefined;
|
|
63
|
+
#footer: Component | undefined;
|
|
64
|
+
#gap: number;
|
|
65
|
+
#minContentLines: number;
|
|
66
|
+
#fitContent: ((maxLines: number | undefined) => void) | undefined;
|
|
67
|
+
#maxHeight: number | undefined;
|
|
68
|
+
#contentRowStart = -1;
|
|
69
|
+
#contentRowCount = 0;
|
|
70
|
+
#cachedLines: readonly string[] | undefined;
|
|
71
|
+
|
|
72
|
+
constructor(options: WizardStepOptions) {
|
|
73
|
+
this.#kind = options.kind;
|
|
74
|
+
this.#content = options.content;
|
|
75
|
+
this.#heading = options.heading;
|
|
76
|
+
this.#intro = options.intro;
|
|
77
|
+
this.#preview = options.preview;
|
|
78
|
+
this.#status = options.status;
|
|
79
|
+
this.#footer = options.footer;
|
|
80
|
+
this.#gap = Math.max(0, Math.floor(options.gap ?? 1));
|
|
81
|
+
this.#minContentLines = Math.max(0, Math.floor(options.minContentLines ?? 1));
|
|
82
|
+
this.#fitContent = options.fitContent;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
get debugChildren(): readonly Component[] {
|
|
86
|
+
return [this.#heading, this.#intro, this.#preview?.component, this.#content, this.#status, this.#footer].filter(
|
|
87
|
+
(component): component is Component => component !== undefined,
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
debugState(): Record<string, unknown> {
|
|
92
|
+
return {
|
|
93
|
+
kind: this.#kind,
|
|
94
|
+
maxHeight: this.#maxHeight ?? null,
|
|
95
|
+
contentRowStart: this.#contentRowStart,
|
|
96
|
+
contentRowCount: this.#contentRowCount,
|
|
97
|
+
previewOptional: this.#preview?.optional === true,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** Update the semantic role exposed through debug state. */
|
|
102
|
+
setKind(kind: WizardStepKind): void {
|
|
103
|
+
this.#kind = kind;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** Replace or remove the heading slot without rebuilding the step. */
|
|
107
|
+
setHeading(heading: Component | undefined): void {
|
|
108
|
+
this.#heading = heading;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** Replace or remove the introductory slot without rebuilding the step. */
|
|
112
|
+
setIntro(intro: Component | undefined): void {
|
|
113
|
+
this.#intro = intro;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** Replace or remove the preview slot without rebuilding the step. */
|
|
117
|
+
setPreview(preview: WizardStepSlot | undefined): void {
|
|
118
|
+
this.#preview = preview;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** Replace the interactive content while retaining layout and height state. */
|
|
122
|
+
setContent(content: Component): void {
|
|
123
|
+
this.#content = content;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** Replace or remove the status slot without rebuilding the step. */
|
|
127
|
+
setStatus(status: Component | undefined): void {
|
|
128
|
+
this.#status = status;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** Replace or remove the footer slot without rebuilding the step. */
|
|
132
|
+
setFooter(footer: Component | undefined): void {
|
|
133
|
+
this.#footer = footer;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** Bound the complete step, including heading, preview, status, and footer. */
|
|
137
|
+
setMaxHeight(maxHeight: number | undefined): void {
|
|
138
|
+
const next = maxHeight === undefined ? undefined : Math.max(0, Math.floor(maxHeight));
|
|
139
|
+
if (next === this.#maxHeight) return;
|
|
140
|
+
this.#maxHeight = next;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
invalidate(): void {
|
|
144
|
+
this.#cachedLines = undefined;
|
|
145
|
+
for (const child of this.debugChildren) child.invalidate?.();
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
dispose(): void {
|
|
149
|
+
for (const child of this.debugChildren) child.dispose?.();
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
handleInput(data: string): void {
|
|
153
|
+
this.#content.handleInput?.(data);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
routeMouse(event: SgrMouseEvent, line: number, col: number): void {
|
|
157
|
+
const content = this.#content;
|
|
158
|
+
if (!isMouseRoutable(content)) return;
|
|
159
|
+
if (event.wheel === null) {
|
|
160
|
+
if (this.#contentRowStart < 0) return;
|
|
161
|
+
if (line < this.#contentRowStart || line >= this.#contentRowStart + this.#contentRowCount) return;
|
|
162
|
+
}
|
|
163
|
+
content.routeMouse(event, line - this.#contentRowStart, col);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
render(width: number): readonly string[] {
|
|
167
|
+
const safeWidth = Math.max(1, width);
|
|
168
|
+
const gap = this.#gap;
|
|
169
|
+
const maxHeight = this.#maxHeight;
|
|
170
|
+
|
|
171
|
+
const heading = this.#renderSlot(this.#heading, safeWidth);
|
|
172
|
+
const intro = this.#renderSlot(this.#intro, safeWidth);
|
|
173
|
+
let preview = this.#renderSlot(this.#preview?.component, safeWidth);
|
|
174
|
+
const status = this.#renderSlot(this.#status, safeWidth);
|
|
175
|
+
const footer = this.#renderSlot(this.#footer, safeWidth);
|
|
176
|
+
|
|
177
|
+
const fixedWithoutPreview = [heading, intro, status, footer].filter(
|
|
178
|
+
(slot): slot is RenderedSlot => slot !== undefined && slot.lines.length > 0,
|
|
179
|
+
);
|
|
180
|
+
if (preview && preview.lines.length > 0 && this.#preview?.optional && maxHeight !== undefined) {
|
|
181
|
+
const requiredSlots = fixedWithoutPreview.length + 1;
|
|
182
|
+
const requiredRows =
|
|
183
|
+
fixedWithoutPreview.reduce((total, slot) => total + slot.lines.length, 0) +
|
|
184
|
+
Math.max(0, requiredSlots - 1) * gap +
|
|
185
|
+
this.#minContentLines;
|
|
186
|
+
const rowsWithPreview = requiredRows + preview.lines.length + gap;
|
|
187
|
+
if (rowsWithPreview > maxHeight) preview = undefined;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const surrounding = [heading, intro, preview, status, footer].filter(
|
|
191
|
+
(slot): slot is RenderedSlot => slot !== undefined && slot.lines.length > 0,
|
|
192
|
+
);
|
|
193
|
+
const slotCountWithContent = surrounding.length + 1;
|
|
194
|
+
const surroundingRows = surrounding.reduce((total, slot) => total + slot.lines.length, 0);
|
|
195
|
+
const gapRows = Math.max(0, slotCountWithContent - 1) * gap;
|
|
196
|
+
const contentBudget = maxHeight === undefined ? undefined : Math.max(0, maxHeight - surroundingRows - gapRows);
|
|
197
|
+
this.#fitContent?.(contentBudget);
|
|
198
|
+
const contentLines = this.#content.render(safeWidth);
|
|
199
|
+
const visibleContent = contentBudget === undefined ? contentLines : contentLines.slice(0, contentBudget);
|
|
200
|
+
|
|
201
|
+
const ordered = [
|
|
202
|
+
heading,
|
|
203
|
+
intro,
|
|
204
|
+
preview,
|
|
205
|
+
{ component: this.#content, lines: visibleContent },
|
|
206
|
+
status,
|
|
207
|
+
footer,
|
|
208
|
+
].filter((slot): slot is RenderedSlot => slot !== undefined && slot.lines.length > 0);
|
|
209
|
+
const lines: string[] = [];
|
|
210
|
+
this.#contentRowStart = -1;
|
|
211
|
+
this.#contentRowCount = visibleContent.length;
|
|
212
|
+
for (const slot of ordered) {
|
|
213
|
+
if (lines.length > 0 && gap > 0) {
|
|
214
|
+
for (let i = 0; i < gap; i++) lines.push("");
|
|
215
|
+
}
|
|
216
|
+
if (slot.component === this.#content) this.#contentRowStart = lines.length;
|
|
217
|
+
lines.push(...slot.lines);
|
|
218
|
+
}
|
|
219
|
+
const bounded = maxHeight === undefined || lines.length <= maxHeight ? lines : lines.slice(0, maxHeight);
|
|
220
|
+
if (this.#cachedLines && sameLines(this.#cachedLines, bounded)) return this.#cachedLines;
|
|
221
|
+
this.#cachedLines = bounded;
|
|
222
|
+
return bounded;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
#renderSlot(component: Component | undefined, width: number): RenderedSlot | undefined {
|
|
226
|
+
if (!component) return undefined;
|
|
227
|
+
return { component, lines: component.render(width) };
|
|
228
|
+
}
|
|
229
|
+
}
|