@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,934 @@
|
|
|
1
|
+
import type { EditorTopBorder } from "../components/composer/types";
|
|
2
|
+
import { Spacer } from "../components/spacer";
|
|
3
|
+
import { isInsideTerminalMultiplexer } from "../terminal-multiplexer";
|
|
4
|
+
import { ProcessTerminal, type Terminal } from "../terminal";
|
|
5
|
+
import {
|
|
6
|
+
type Component,
|
|
7
|
+
Container,
|
|
8
|
+
type ResizeScrollbackMode,
|
|
9
|
+
type TerminalFramePlan,
|
|
10
|
+
type TerminalFrameProvider,
|
|
11
|
+
TUI,
|
|
12
|
+
type TUIOptions,
|
|
13
|
+
type ViewportSize,
|
|
14
|
+
} from "../tui";
|
|
15
|
+
import { sliceWithWidth, truncateToWidth, visibleWidth } from "../utils";
|
|
16
|
+
import { postmortem } from "@oh-my-pi/pi-utils";
|
|
17
|
+
import { CustomEditor } from "./custom-editor";
|
|
18
|
+
import { type AnimationFrame, TranscriptContainer } from "../chrome/transcript-container";
|
|
19
|
+
import { type LspServerInfo, type RecentSession, WelcomeComponent } from "./welcome";
|
|
20
|
+
import { ensureThemeSync, getEditorTheme, theme } from "../theme/theme";
|
|
21
|
+
|
|
22
|
+
const DOUBLE_INTERRUPT_MS = 500;
|
|
23
|
+
|
|
24
|
+
/** Live settings that affect the composer before and after session adoption. */
|
|
25
|
+
export interface ComposerPreferences {
|
|
26
|
+
readonly quiet: boolean;
|
|
27
|
+
readonly composerShape: string;
|
|
28
|
+
readonly showHardwareCursor: boolean;
|
|
29
|
+
readonly maxInlineImages: number;
|
|
30
|
+
readonly resizeScrollback: ResizeScrollbackMode;
|
|
31
|
+
readonly imeSafeCursor: boolean;
|
|
32
|
+
readonly autocompleteMaxVisible: number;
|
|
33
|
+
readonly spellingTypoDetection: boolean;
|
|
34
|
+
readonly spellingAutocomplete: boolean;
|
|
35
|
+
readonly spellingAutocorrect: boolean;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Settings-schema-compatible defaults used when constructing a dependency-free composer. */
|
|
39
|
+
export const COMPOSER_DEFAULTS: ComposerPreferences = {
|
|
40
|
+
quiet: false,
|
|
41
|
+
composerShape: "band",
|
|
42
|
+
showHardwareCursor: true,
|
|
43
|
+
maxInlineImages: 8,
|
|
44
|
+
resizeScrollback: "rebuild",
|
|
45
|
+
imeSafeCursor: false,
|
|
46
|
+
autocompleteMaxVisible: 10,
|
|
47
|
+
spellingTypoDetection: true,
|
|
48
|
+
spellingAutocomplete: true,
|
|
49
|
+
spellingAutocorrect: false,
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/** Welcome data that can be supplied initially or patched as startup resolves it. */
|
|
53
|
+
export interface ComposerWelcomeUpdate {
|
|
54
|
+
readonly version?: string;
|
|
55
|
+
readonly modelName?: string;
|
|
56
|
+
readonly providerName?: string;
|
|
57
|
+
readonly recentSessions?: readonly RecentSession[];
|
|
58
|
+
readonly lspServers?: readonly LspServerInfo[];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Placeholder-only status chrome replayed on the next first frame so the
|
|
63
|
+
* status band/border exists before the session-aware status line attaches.
|
|
64
|
+
* Bound to the composer shape it was rendered for; a different shape drops it.
|
|
65
|
+
*/
|
|
66
|
+
export interface ComposerStatusSnapshot {
|
|
67
|
+
readonly shape: string;
|
|
68
|
+
/** ANSI wrapper of the editor border at snapshot time (session accent or thinking color). */
|
|
69
|
+
readonly borderColor?: {
|
|
70
|
+
readonly prefix: string;
|
|
71
|
+
readonly suffix: string;
|
|
72
|
+
};
|
|
73
|
+
/** Status content embedded in the editor's top chrome (`top-border`, `top-band`, `top-rule-chip`). */
|
|
74
|
+
readonly topBorder?: {
|
|
75
|
+
readonly content: string;
|
|
76
|
+
readonly width: number;
|
|
77
|
+
};
|
|
78
|
+
/** Standalone bottom-bar rows (`pi`/`claude` shapes), gap row included. */
|
|
79
|
+
readonly bottomLines: readonly string[];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Optional dependencies and initial state for a standalone composer. */
|
|
83
|
+
export interface ComposerOptions {
|
|
84
|
+
readonly terminal?: Terminal;
|
|
85
|
+
/** Extra TUI construction options (render scheduler injection for tests and `omp render`). */
|
|
86
|
+
readonly tuiOptions?: TUIOptions;
|
|
87
|
+
readonly preferences?: Partial<ComposerPreferences>;
|
|
88
|
+
readonly welcome?: ComposerWelcomeUpdate;
|
|
89
|
+
readonly status?: ComposerStatusSnapshot;
|
|
90
|
+
readonly exit?: (code: number) => void;
|
|
91
|
+
readonly now?: () => number;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Controls the first terminal paint for a composer that does not already own the terminal. */
|
|
95
|
+
export interface ComposerStartOptions {
|
|
96
|
+
readonly clearScrollback?: boolean;
|
|
97
|
+
readonly playWelcomeIntro?: boolean;
|
|
98
|
+
/**
|
|
99
|
+
* Paint without owning stdin: the tty keeps cooked-mode echo/editing so
|
|
100
|
+
* typing stays visible while startup module loading blocks the event loop.
|
|
101
|
+
* {@link Composer.enableInput} later switches to raw input and replays the
|
|
102
|
+
* kernel-buffered keystrokes into the editor.
|
|
103
|
+
*/
|
|
104
|
+
readonly deferInput?: boolean;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Mount slot for the session-aware status component below the editor. Shows
|
|
109
|
+
* placeholder rows during startup until the real component mounts.
|
|
110
|
+
*/
|
|
111
|
+
class StatusHost implements Component {
|
|
112
|
+
#lines: readonly string[] = [];
|
|
113
|
+
#component: Component | undefined;
|
|
114
|
+
|
|
115
|
+
get mounted(): boolean {
|
|
116
|
+
return this.#component !== undefined;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
setLines(lines: readonly string[]): void {
|
|
120
|
+
this.#lines = lines;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
setComponent(component: Component): void {
|
|
124
|
+
this.#component = component;
|
|
125
|
+
this.#lines = [];
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
render(width: number): readonly string[] {
|
|
129
|
+
if (this.#component) return this.#component.render(width);
|
|
130
|
+
return this.#lines.map(line => truncateToWidth(line, width));
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/** One click target's row span within the mutable viewport (half-open `[start, end)`). */
|
|
135
|
+
export interface ViewportClickSpan {
|
|
136
|
+
start: number;
|
|
137
|
+
end: number;
|
|
138
|
+
/** Candidate subagent ids for a span-local row. */
|
|
139
|
+
candidates: (local: number) => string[];
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Row-level click target: maps rendered rows to subagent ids. Implemented by
|
|
144
|
+
* the subagent HUD, whose rows are fixed 1:1 with visible sessions.
|
|
145
|
+
*/
|
|
146
|
+
export interface ViewportClickRowTarget {
|
|
147
|
+
getClickAgentAtRow(row: number): string | undefined;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Candidates under a mutable-viewport line: the first span containing it.
|
|
152
|
+
* Pure seam for tests; the caller intersects with the live registry, which
|
|
153
|
+
* decides focusability and recency.
|
|
154
|
+
*/
|
|
155
|
+
export function routeViewportClick(spans: readonly ViewportClickSpan[], index: number): string[] {
|
|
156
|
+
if (!Number.isInteger(index) || index < 0) return [];
|
|
157
|
+
for (const span of spans) {
|
|
158
|
+
if (index < span.start || index >= span.end) continue;
|
|
159
|
+
return span.candidates(index - span.start);
|
|
160
|
+
}
|
|
161
|
+
return [];
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Reserved click-candidate id for the pinned HUD expander row. Checked before
|
|
166
|
+
* any registry lookup: its `@…:…` charset cannot collide with generated agent
|
|
167
|
+
* ids (word names, numeric and `-N` suffixes, dotted nesting).
|
|
168
|
+
*/
|
|
169
|
+
export const PINNED_HUD_TOGGLE_ID = "@omp:toggle-pinned-hud";
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Nested background opens inside a hovered row. The band wraps the line, so a
|
|
173
|
+
* surviving nested open would paint over it for every cell it covers; the
|
|
174
|
+
* matching closes stay and become band resumes via bgFill.
|
|
175
|
+
*/
|
|
176
|
+
const NESTED_BG_OPEN_PATTERN = new RegExp(`${String.fromCharCode(27)}\\[(?:4[0-7]|10[0-7]|48;[0-9;]*)m`, "g");
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Candidate resolver for a row-level click target, if the component is one.
|
|
180
|
+
* Shared by root and nested-child hit-testing so both stay in lockstep.
|
|
181
|
+
*/
|
|
182
|
+
function rowTargetCandidates(target: Component): ((local: number) => string[]) | undefined {
|
|
183
|
+
const rowTarget = target as Partial<ViewportClickRowTarget>;
|
|
184
|
+
if (typeof rowTarget.getClickAgentAtRow !== "function") return undefined;
|
|
185
|
+
return (local: number) => {
|
|
186
|
+
const id = rowTarget.getClickAgentAtRow?.(local);
|
|
187
|
+
return id === undefined ? [] : [id];
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Canonical interactive composer, usable before session/settings exist and updatable in place.
|
|
193
|
+
* It owns the terminal, welcome header, and editor; InteractiveMode later supplies authoritative
|
|
194
|
+
* data and mounts the session-aware runtime children without replacing the visible header.
|
|
195
|
+
*/
|
|
196
|
+
export class Composer implements TerminalFrameProvider {
|
|
197
|
+
readonly ui: TUI;
|
|
198
|
+
#editor: CustomEditor;
|
|
199
|
+
readonly #header = new Container();
|
|
200
|
+
readonly #bootstrapInputGap = new Spacer(1);
|
|
201
|
+
readonly #statusHost = new StatusHost();
|
|
202
|
+
readonly #exit: (code: number) => void;
|
|
203
|
+
readonly #now: () => number;
|
|
204
|
+
#preferences: ComposerPreferences;
|
|
205
|
+
#welcome: WelcomeComponent | undefined;
|
|
206
|
+
#version = "";
|
|
207
|
+
#modelName = "";
|
|
208
|
+
#providerName = "";
|
|
209
|
+
#recentSessions: RecentSession[] = [];
|
|
210
|
+
#lspServers: LspServerInfo[] = [];
|
|
211
|
+
#headerBefore: readonly Component[] = [];
|
|
212
|
+
#headerAfter: readonly Component[] = [];
|
|
213
|
+
#runtimeChildren: readonly Component[] = [];
|
|
214
|
+
#statusSnapshot: ComposerStatusSnapshot | undefined;
|
|
215
|
+
#runtimeMounted = false;
|
|
216
|
+
// Composer-owned history id space. Transcript batch ids restart across
|
|
217
|
+
// container clears/swaps; the composer translates them into one monotonic
|
|
218
|
+
// sequence the terminal's accepted-id watermark can trust.
|
|
219
|
+
#nextHistoryId = 1;
|
|
220
|
+
#offeredHistory:
|
|
221
|
+
| {
|
|
222
|
+
id: number;
|
|
223
|
+
rows: readonly string[];
|
|
224
|
+
kind: "append" | "replay";
|
|
225
|
+
source:
|
|
226
|
+
| "header"
|
|
227
|
+
| {
|
|
228
|
+
transcript: TranscriptContainer;
|
|
229
|
+
transcriptId?: number;
|
|
230
|
+
header: "none" | "replay";
|
|
231
|
+
/** Recomposed header rows to accept as the new retired-header bytes. */
|
|
232
|
+
headerRows?: readonly string[];
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
| undefined;
|
|
236
|
+
#historyReplayRequested = false;
|
|
237
|
+
#headerReplayPending = false;
|
|
238
|
+
#historyFlush = false;
|
|
239
|
+
// The welcome header retires to terminal history exactly once, after the
|
|
240
|
+
// intro settles; until then it renders as mutable viewport chrome.
|
|
241
|
+
#headerRetired = false;
|
|
242
|
+
// Exact hard rows accepted into native history. Transient resize-alt
|
|
243
|
+
// paints reflow these rows to match the terminal's own rewrap of history
|
|
244
|
+
// it still holds; a settled replay owns every byte it emits, so it
|
|
245
|
+
// recomposes the header at the replay width and refreshes these rows.
|
|
246
|
+
#retiredHeaderRows: readonly string[] | undefined;
|
|
247
|
+
/** Click spans of the last `renderFrame` viewport, in viewport coordinates. */
|
|
248
|
+
#lastClickSpans: ViewportClickSpan[] = [];
|
|
249
|
+
/** Click-candidate id under the pointer, painted with the hover band. Id-anchored so it follows streaming rows. */
|
|
250
|
+
#hoveredClickId: string | undefined;
|
|
251
|
+
// Hard-row prefix currently above the native viewport. The first resize
|
|
252
|
+
// frame may pull part of it down before the normal buffer is borrowed.
|
|
253
|
+
#retiredHeaderStart = 0;
|
|
254
|
+
#resizeRetiredHeaderStart: number | undefined;
|
|
255
|
+
#lastNormalRows = 0;
|
|
256
|
+
// Smallest below-transcript chrome height (editor + status + any transient
|
|
257
|
+
// inline dialog) seen since mount. Retirement is billed against this
|
|
258
|
+
// persistent baseline, never the transient peak, so a dialog or tall editor
|
|
259
|
+
// that later shrinks never leaves committed transcript rows the live viewport
|
|
260
|
+
// cannot reclaim (#11007). The baseline is terminal-height independent — the
|
|
261
|
+
// editor and status floors do not scale with rows — so it is retained across
|
|
262
|
+
// resizes rather than rediscovered from whatever chrome is expanded at the
|
|
263
|
+
// moment the height changes.
|
|
264
|
+
#retirementBelowFloor: number | undefined;
|
|
265
|
+
#lastInterruptAt = 0;
|
|
266
|
+
#started = false;
|
|
267
|
+
#stopped = false;
|
|
268
|
+
#transferred = false;
|
|
269
|
+
|
|
270
|
+
constructor(options: ComposerOptions = {}) {
|
|
271
|
+
ensureThemeSync();
|
|
272
|
+
// Host-owned hard exit: route through postmortem so a double-Ctrl-C during
|
|
273
|
+
// an open extension-load guard window exits cleanly instead of throwing
|
|
274
|
+
// ExtensionExitError through the guarded process.exit (#11789).
|
|
275
|
+
this.#exit = options.exit ?? (code => postmortem.exitProcess(code));
|
|
276
|
+
this.#now = options.now ?? Date.now;
|
|
277
|
+
this.#preferences = { ...COMPOSER_DEFAULTS, ...options.preferences };
|
|
278
|
+
this.#statusSnapshot = options.status;
|
|
279
|
+
this.#applyWelcomeUpdate(options.welcome ?? {});
|
|
280
|
+
|
|
281
|
+
this.ui = new TUI(
|
|
282
|
+
options.terminal ?? new ProcessTerminal(),
|
|
283
|
+
this.#preferences.showHardwareCursor,
|
|
284
|
+
options.tuiOptions,
|
|
285
|
+
);
|
|
286
|
+
this.ui.setFrameProvider(this);
|
|
287
|
+
this.ui.setMaxInlineImages(this.#preferences.maxInlineImages);
|
|
288
|
+
this.ui.setResizeScrollback(this.#preferences.resizeScrollback);
|
|
289
|
+
|
|
290
|
+
this.#editor = new CustomEditor(getEditorTheme());
|
|
291
|
+
this.editor.disableSubmit = true;
|
|
292
|
+
this.editor.setUseTerminalCursor(this.ui.getShowHardwareCursor());
|
|
293
|
+
this.editor.setImeSafeCursorLayout(this.#preferences.imeSafeCursor);
|
|
294
|
+
this.editor.setAutocompleteMaxVisible(this.#preferences.autocompleteMaxVisible);
|
|
295
|
+
this.editor.setSpellingFeatures({
|
|
296
|
+
typoDetection: this.#preferences.spellingTypoDetection,
|
|
297
|
+
autocomplete: this.#preferences.spellingAutocomplete,
|
|
298
|
+
autocorrect: this.#preferences.spellingAutocorrect,
|
|
299
|
+
});
|
|
300
|
+
try {
|
|
301
|
+
this.editor.setBorderStyle(this.#preferences.composerShape);
|
|
302
|
+
} catch {
|
|
303
|
+
// Extension-defined styles arrive with the session; InteractiveMode reapplies them.
|
|
304
|
+
}
|
|
305
|
+
this.#applyStatusSnapshot();
|
|
306
|
+
// Emergency controls stay active until InteractiveMode installs configured bindings.
|
|
307
|
+
// They deliberately mirror the interactive editor's contract so a stalled startup
|
|
308
|
+
// never behaves differently from a healthy one: Ctrl+C clears the draft and a second
|
|
309
|
+
// press exits 130 — the unconditional abort, draft or not; Ctrl+D exits 0 on an
|
|
310
|
+
// empty draft and otherwise forward-deletes (see CustomEditor's app.exit handling),
|
|
311
|
+
// so typing while the session loads cannot be lost to a mistyped delete.
|
|
312
|
+
this.editor.setActionKeys("app.clear", ["ctrl+c"]);
|
|
313
|
+
this.editor.setActionKeys("app.exit", ["ctrl+d"]);
|
|
314
|
+
this.editor.onClear = () => this.#handleInterrupt();
|
|
315
|
+
this.editor.onExit = () => this.#requestExit(0);
|
|
316
|
+
this.editor.setShimmerRepaintHandler(() => this.ui.requestComponentRender(this.editor));
|
|
317
|
+
|
|
318
|
+
if (!this.#preferences.quiet) this.#ensureWelcome();
|
|
319
|
+
this.#rebuildHeader();
|
|
320
|
+
this.ui.addChild(this.#header);
|
|
321
|
+
this.ui.addChild(this.#bootstrapInputGap);
|
|
322
|
+
this.ui.addChild(this.editor);
|
|
323
|
+
this.ui.addChild(this.#statusHost);
|
|
324
|
+
this.ui.setFocus(this.editor);
|
|
325
|
+
}
|
|
326
|
+
/** Compose the bounded mutable viewport and the next ordered history append. */
|
|
327
|
+
renderFrame(viewport: ViewportSize): TerminalFramePlan {
|
|
328
|
+
if (!this.#started || this.#stopped) return { viewport: [] };
|
|
329
|
+
const width = Math.max(1, viewport.columns);
|
|
330
|
+
const rows = Math.max(0, viewport.rows);
|
|
331
|
+
if (this.#resizeRetiredHeaderStart !== undefined) {
|
|
332
|
+
this.#retiredHeaderStart = this.#resizeRetiredHeaderStart;
|
|
333
|
+
this.#resizeRetiredHeaderStart = undefined;
|
|
334
|
+
}
|
|
335
|
+
this.#lastNormalRows = rows;
|
|
336
|
+
const roots = this.#runtimeMounted
|
|
337
|
+
? [...this.#runtimeChildren, this.#statusHost]
|
|
338
|
+
: [this.#header, this.#bootstrapInputGap, this.editor, this.#statusHost];
|
|
339
|
+
const transcriptIndex = roots.findIndex(root => root instanceof TranscriptContainer);
|
|
340
|
+
if (transcriptIndex < 0) {
|
|
341
|
+
this.#lastClickSpans = [];
|
|
342
|
+
return { viewport: this.#renderRoots(roots, width).slice(-rows) };
|
|
343
|
+
}
|
|
344
|
+
const transcript = roots[transcriptIndex] as TranscriptContainer;
|
|
345
|
+
const preRoots = this.#renderRoots(roots.slice(0, transcriptIndex), width);
|
|
346
|
+
const afterRoots = roots.slice(transcriptIndex + 1);
|
|
347
|
+
const after: string[] = [];
|
|
348
|
+
const afterSpans: ViewportClickSpan[] = [];
|
|
349
|
+
for (const root of afterRoots) {
|
|
350
|
+
const start = after.length;
|
|
351
|
+
// Row targets usually nest one level down: chrome roots are plain
|
|
352
|
+
// containers (the HUD lives inside `subagentContainer`), and
|
|
353
|
+
// `Container.render` is a pure concatenation, so child spans tile
|
|
354
|
+
// the root span exactly. Render those children once and share the
|
|
355
|
+
// rows for composition and measurement — a second render per frame
|
|
356
|
+
// would duplicate render-time side effects (image placement
|
|
357
|
+
// registration). Roots with a custom render keep the composed
|
|
358
|
+
// output as the source of truth and measure up to the last target.
|
|
359
|
+
const plainContainer = root instanceof Container && root.render === Container.prototype.render;
|
|
360
|
+
const targets = root instanceof Container ? root.children : [root];
|
|
361
|
+
const resolves = targets.map(rowTargetCandidates);
|
|
362
|
+
const lastTarget = resolves.findLastIndex(resolve => resolve !== undefined);
|
|
363
|
+
if (plainContainer) {
|
|
364
|
+
let offset = start;
|
|
365
|
+
for (let index = 0; index < targets.length; index++) {
|
|
366
|
+
const childLines = targets[index]!.render(width);
|
|
367
|
+
after.push(...childLines);
|
|
368
|
+
if (index > lastTarget) continue;
|
|
369
|
+
const resolve = resolves[index];
|
|
370
|
+
if (resolve !== undefined && childLines.length > 0) {
|
|
371
|
+
afterSpans.push({ start: offset, end: offset + childLines.length, candidates: resolve });
|
|
372
|
+
}
|
|
373
|
+
offset += childLines.length;
|
|
374
|
+
}
|
|
375
|
+
continue;
|
|
376
|
+
}
|
|
377
|
+
after.push(...root.render(width));
|
|
378
|
+
if (lastTarget === -1) continue;
|
|
379
|
+
let offset = start;
|
|
380
|
+
for (let index = 0; index <= lastTarget; index++) {
|
|
381
|
+
const childLines = targets[index] === root ? after.length - start : targets[index]!.render(width).length;
|
|
382
|
+
const resolve = resolves[index];
|
|
383
|
+
if (resolve !== undefined && childLines > 0) {
|
|
384
|
+
afterSpans.push({ start: offset, end: offset + childLines, candidates: resolve });
|
|
385
|
+
}
|
|
386
|
+
offset += childLines;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
// Offer history under capacity pressure only: blocks stay live (and keep
|
|
390
|
+
// reflowing to the current width) while the screen has room. A batch
|
|
391
|
+
// leaves the mutable viewport in the same frame it is appended, so its
|
|
392
|
+
// rows are never painted twice.
|
|
393
|
+
//
|
|
394
|
+
// Retirement is billed against the persistent below-transcript chrome
|
|
395
|
+
// baseline, not the transient peak: a confirmation dialog or a tall
|
|
396
|
+
// multi-line editor swapped in below the transcript clips the live tail
|
|
397
|
+
// for its lifetime, but must not permanently commit transcript rows to
|
|
398
|
+
// native history — otherwise a later shrink cannot refill the freed rows
|
|
399
|
+
// and the editor drifts up above a band of blank rows (#11007).
|
|
400
|
+
this.#retirementBelowFloor =
|
|
401
|
+
this.#retirementBelowFloor === undefined ? after.length : Math.min(this.#retirementBelowFloor, after.length);
|
|
402
|
+
const belowFloor = this.#retirementBelowFloor;
|
|
403
|
+
const history = this.#offerHistory(transcript, width, rows, preRoots.length + belowFloor);
|
|
404
|
+
const headerVisible = !this.#headerRetired && this.#offeredHistory?.source !== "header";
|
|
405
|
+
const headerRows = headerVisible ? this.#header.render(width) : [];
|
|
406
|
+
const before = [...headerRows, ...preRoots];
|
|
407
|
+
const now = performance.now();
|
|
408
|
+
const frame: AnimationFrame = { now, tick: Math.floor(now / 80) };
|
|
409
|
+
// The live tail is laid out against the same baseline retirement is
|
|
410
|
+
// billed against, so its compaction allocator (one row per block, no
|
|
411
|
+
// inter-block blanks) engages only when a block genuinely cannot retire.
|
|
412
|
+
// Rows the transient chrome peak displaces are clipped from the top by
|
|
413
|
+
// the `drop` slice below, which is what scrollback would have done.
|
|
414
|
+
const active = transcript.renderViewport(width, Math.max(0, rows - before.length - belowFloor), frame);
|
|
415
|
+
const activeSpans: ViewportClickSpan[] = [];
|
|
416
|
+
for (const span of transcript.getLastViewportSpans()) {
|
|
417
|
+
const ids = (span.component as Partial<{ getClickFocusAgentIds(): string[] }>).getClickFocusAgentIds?.();
|
|
418
|
+
if (!ids || ids.length === 0) continue;
|
|
419
|
+
activeSpans.push({ start: span.start, end: span.end, candidates: () => ids });
|
|
420
|
+
}
|
|
421
|
+
const drop = Math.max(0, before.length + active.length + after.length - rows);
|
|
422
|
+
const mutable = [...before, ...active, ...after].slice(drop);
|
|
423
|
+
const viewportLength = mutable.length;
|
|
424
|
+
const spans: ViewportClickSpan[] = [];
|
|
425
|
+
const shift = (span: ViewportClickSpan, base: number): void => {
|
|
426
|
+
const start = span.start + base;
|
|
427
|
+
const end = Math.min(span.end + base, viewportLength);
|
|
428
|
+
const clamped = Math.max(0, start);
|
|
429
|
+
if (end > clamped) {
|
|
430
|
+
// A clipped head must offset the callback: without the skew the
|
|
431
|
+
// first visible row would hit-test as span-local row 0.
|
|
432
|
+
const skew = clamped - start;
|
|
433
|
+
spans.push({ start: clamped, end, candidates: (local: number) => span.candidates(local + skew) });
|
|
434
|
+
}
|
|
435
|
+
};
|
|
436
|
+
for (const span of activeSpans) shift(span, before.length - drop);
|
|
437
|
+
for (const span of afterSpans) shift(span, before.length + active.length - drop);
|
|
438
|
+
this.#lastClickSpans = spans;
|
|
439
|
+
if (history !== undefined && this.#offeredHistory?.source === "header") {
|
|
440
|
+
const visibleHeaderRows = Math.max(0, rows - (mutable.length + drop));
|
|
441
|
+
this.#retiredHeaderStart = Math.max(0, history.rows.length - visibleHeaderRows);
|
|
442
|
+
}
|
|
443
|
+
return { history, viewport: this.#paintHoverBand(mutable, spans) };
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* Band the hovered click target's current rows. Id-anchored (not
|
|
448
|
+
* line-anchored) so the band follows an agent whose rows shift while it
|
|
449
|
+
* streams; a retired id matches no span and simply paints nothing. Only
|
|
450
|
+
* the viewport copy is banded — retirement reads unbanded component rows.
|
|
451
|
+
*/
|
|
452
|
+
#paintHoverBand(viewport: string[], spans: readonly ViewportClickSpan[]): string[] {
|
|
453
|
+
const hovered = this.#hoveredClickId;
|
|
454
|
+
if (hovered === undefined) return viewport;
|
|
455
|
+
let banded = false;
|
|
456
|
+
const painted = viewport.map((line, index) => {
|
|
457
|
+
for (const span of spans) {
|
|
458
|
+
if (index < span.start || index >= span.end) continue;
|
|
459
|
+
if (!span.candidates(index - span.start).includes(hovered)) continue;
|
|
460
|
+
banded = true;
|
|
461
|
+
// A wrapping band loses to background opens nested inside the row
|
|
462
|
+
// (live card rows carry the pending-tint bg, which would paint over
|
|
463
|
+
// the band for every cell it covers), so drop nested bg opens
|
|
464
|
+
// first; their closes stay and become band resumes via bgFill.
|
|
465
|
+
return theme.bgFill("selectedBg", line.replace(NESTED_BG_OPEN_PATTERN, ""));
|
|
466
|
+
}
|
|
467
|
+
return line;
|
|
468
|
+
});
|
|
469
|
+
return banded ? painted : viewport;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
/**
|
|
473
|
+
* Candidate subagent ids under a mutable-viewport line, for click-to-focus.
|
|
474
|
+
* Empty when the line has no click target (chrome, separators, retired rows
|
|
475
|
+
* are never in the viewport). Callers intersect with the live registry.
|
|
476
|
+
*/
|
|
477
|
+
viewportClickCandidates(index: number): string[] {
|
|
478
|
+
return routeViewportClick(this.#lastClickSpans, index);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* Point the hover band at a click-candidate id (or clear it). Takes effect
|
|
483
|
+
* on the next frame; callers repaint only when the target actually changes.
|
|
484
|
+
*/
|
|
485
|
+
setHoveredClickId(id: string | undefined): void {
|
|
486
|
+
this.#hoveredClickId = id;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
/** Acknowledges one accepted header, replay, or transcript batch. */
|
|
490
|
+
acknowledgeHistory(id: number): void {
|
|
491
|
+
const offered = this.#offeredHistory;
|
|
492
|
+
if (offered === undefined || offered.id !== id) return;
|
|
493
|
+
if (offered.source === "header") {
|
|
494
|
+
this.#headerRetired = true;
|
|
495
|
+
this.#retiredHeaderRows = offered.rows;
|
|
496
|
+
} else {
|
|
497
|
+
if (offered.source.transcriptId !== undefined) {
|
|
498
|
+
offered.source.transcript.acknowledgeFinalizedBatch(offered.source.transcriptId);
|
|
499
|
+
}
|
|
500
|
+
if (offered.source.header === "replay") {
|
|
501
|
+
this.#headerReplayPending = false;
|
|
502
|
+
if (offered.source.headerRows !== undefined) this.#retiredHeaderRows = offered.source.headerRows;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
this.#offeredHistory = undefined;
|
|
506
|
+
if (this.#historyReplayRequested) this.#startHistoryReplay();
|
|
507
|
+
if (offered.kind === "replay") {
|
|
508
|
+
this.ui.requestRender();
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
/** Render the semantic transcript tail while the terminal borrows its resize buffer. */
|
|
513
|
+
renderResizeFrame(viewport: ViewportSize): readonly string[] {
|
|
514
|
+
if (!this.#started || this.#stopped) return [];
|
|
515
|
+
const width = Math.max(1, viewport.columns);
|
|
516
|
+
const rows = Math.max(0, viewport.rows);
|
|
517
|
+
const tail = this.#runtimeMounted
|
|
518
|
+
? this.#renderResizeTail(width, rows)
|
|
519
|
+
: this.#renderRoots([this.#bootstrapInputGap, this.editor, this.#statusHost], width);
|
|
520
|
+
let header: readonly string[];
|
|
521
|
+
if (this.#headerRetired) {
|
|
522
|
+
this.#resizeRetiredHeaderStart ??= Math.max(
|
|
523
|
+
0,
|
|
524
|
+
this.#retiredHeaderStart - Math.max(0, rows - this.#lastNormalRows),
|
|
525
|
+
);
|
|
526
|
+
header = this.#reflowRetiredHeader(width, this.#resizeRetiredHeaderStart);
|
|
527
|
+
} else {
|
|
528
|
+
header = this.#header.render(width);
|
|
529
|
+
}
|
|
530
|
+
const rendered = [...header, ...tail];
|
|
531
|
+
return rendered.length <= rows ? rendered : rendered.slice(rendered.length - rows);
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
/** Replays committed presentation without changing logical retirement state. */
|
|
535
|
+
beginHistoryReplay(): void {
|
|
536
|
+
if (this.#offeredHistory !== undefined) {
|
|
537
|
+
this.#historyReplayRequested = true;
|
|
538
|
+
return;
|
|
539
|
+
}
|
|
540
|
+
this.#startHistoryReplay();
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
/** Forces every currently eligible finalized prefix to retire before stop. */
|
|
544
|
+
beginHistoryFlush(): void {
|
|
545
|
+
this.#historyFlush = true;
|
|
546
|
+
// A pending replay would re-render and re-stream the entire committed
|
|
547
|
+
// ledger during shutdown; the terminal already holds that history, so
|
|
548
|
+
// flush emits only genuinely un-retired rows. An already offered batch
|
|
549
|
+
// stays valid and is accepted by the flush loop.
|
|
550
|
+
this.#historyReplayRequested = false;
|
|
551
|
+
this.#headerReplayPending = false;
|
|
552
|
+
for (const child of this.#runtimeChildren) {
|
|
553
|
+
if (child instanceof TranscriptContainer) child.cancelReplay();
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
#startHistoryReplay(): void {
|
|
558
|
+
this.#headerReplayPending = this.#headerRetired && (this.#retiredHeaderRows?.length ?? 0) > 0;
|
|
559
|
+
this.#historyReplayRequested = false;
|
|
560
|
+
for (const child of this.#runtimeChildren) {
|
|
561
|
+
if (child instanceof TranscriptContainer) child.beginReplay();
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
/** Header retires first; replay coalesces it with the complete transcript ledger. */
|
|
566
|
+
#offerHistory(
|
|
567
|
+
transcript: TranscriptContainer,
|
|
568
|
+
width: number,
|
|
569
|
+
rows: number,
|
|
570
|
+
chromeRows: number,
|
|
571
|
+
): { id: number; rows: readonly string[]; kind: "append" | "replay" } | undefined {
|
|
572
|
+
if (this.#offeredHistory !== undefined) {
|
|
573
|
+
this.#rerenderOfferedHistory(width);
|
|
574
|
+
return {
|
|
575
|
+
id: this.#offeredHistory.id,
|
|
576
|
+
rows: this.#offeredHistory.rows,
|
|
577
|
+
kind: this.#offeredHistory.kind,
|
|
578
|
+
};
|
|
579
|
+
}
|
|
580
|
+
if (this.#headerReplayPending) {
|
|
581
|
+
const transcriptReplay = transcript.peekReplayBatch(width);
|
|
582
|
+
// A replay follows a scrollback clear, so the header recomposes at
|
|
583
|
+
// the new width exactly like transcript entries do. An empty
|
|
584
|
+
// recompose (welcome unmounted after retirement) falls back to the
|
|
585
|
+
// committed rows, hard-wrapped the way the terminal would.
|
|
586
|
+
const recomposed = this.#header.render(width);
|
|
587
|
+
const headerRows = recomposed.length > 0 ? [...recomposed, ""] : this.#reflowRetiredHeader(width, 0);
|
|
588
|
+
this.#offeredHistory = {
|
|
589
|
+
id: this.#nextHistoryId++,
|
|
590
|
+
rows: [...headerRows, ...(transcriptReplay?.rows ?? [])],
|
|
591
|
+
kind: "replay",
|
|
592
|
+
source: {
|
|
593
|
+
transcript,
|
|
594
|
+
transcriptId: transcriptReplay?.id,
|
|
595
|
+
header: "replay",
|
|
596
|
+
headerRows,
|
|
597
|
+
},
|
|
598
|
+
};
|
|
599
|
+
return {
|
|
600
|
+
id: this.#offeredHistory.id,
|
|
601
|
+
rows: this.#offeredHistory.rows,
|
|
602
|
+
kind: this.#offeredHistory.kind,
|
|
603
|
+
};
|
|
604
|
+
}
|
|
605
|
+
if (!this.#headerRetired) {
|
|
606
|
+
const welcome = this.#welcome;
|
|
607
|
+
if (welcome !== undefined && !welcome.isTranscriptBlockFinalized()) return undefined;
|
|
608
|
+
// The header stays live viewport chrome until the screen fills; then it
|
|
609
|
+
// retires first so transcript prefixes can follow in order.
|
|
610
|
+
const renderedHeader = this.#header.render(width);
|
|
611
|
+
if (renderedHeader.length > 0) {
|
|
612
|
+
const liveRows = transcript.liveRowCount(width);
|
|
613
|
+
if (!this.#historyFlush && renderedHeader.length + chromeRows + liveRows <= rows) return undefined;
|
|
614
|
+
this.#offeredHistory = {
|
|
615
|
+
id: this.#nextHistoryId++,
|
|
616
|
+
rows: [...renderedHeader, ""],
|
|
617
|
+
kind: "append",
|
|
618
|
+
source: "header",
|
|
619
|
+
};
|
|
620
|
+
return {
|
|
621
|
+
id: this.#offeredHistory.id,
|
|
622
|
+
rows: this.#offeredHistory.rows,
|
|
623
|
+
kind: this.#offeredHistory.kind,
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
this.#headerRetired = true;
|
|
627
|
+
this.#retiredHeaderRows = [];
|
|
628
|
+
}
|
|
629
|
+
const batch = this.#historyFlush
|
|
630
|
+
? transcript.peekFlushBatch(width)
|
|
631
|
+
: transcript.peekFinalizedBatch(width, Math.max(0, rows - chromeRows));
|
|
632
|
+
if (batch === undefined) return undefined;
|
|
633
|
+
this.#offeredHistory = {
|
|
634
|
+
id: this.#nextHistoryId++,
|
|
635
|
+
rows: batch.rows,
|
|
636
|
+
kind: batch.kind ?? "append",
|
|
637
|
+
source: { transcript, transcriptId: batch.id, header: "none" },
|
|
638
|
+
};
|
|
639
|
+
return {
|
|
640
|
+
id: this.#offeredHistory.id,
|
|
641
|
+
rows: this.#offeredHistory.rows,
|
|
642
|
+
kind: this.#offeredHistory.kind,
|
|
643
|
+
};
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
#rerenderOfferedHistory(width: number): void {
|
|
647
|
+
const offered = this.#offeredHistory;
|
|
648
|
+
if (offered === undefined) return;
|
|
649
|
+
if (offered.source === "header") {
|
|
650
|
+
const rows = this.#header.render(width);
|
|
651
|
+
offered.rows = rows.length > 0 ? [...rows, ""] : [];
|
|
652
|
+
return;
|
|
653
|
+
}
|
|
654
|
+
const transcript = offered.source.transcript.rerenderOfferedBatch(width);
|
|
655
|
+
if (offered.source.header === "none") {
|
|
656
|
+
if (transcript !== undefined) offered.rows = transcript.rows;
|
|
657
|
+
return;
|
|
658
|
+
}
|
|
659
|
+
const recomposed = this.#header.render(width);
|
|
660
|
+
const headerRows = recomposed.length > 0 ? [...recomposed, ""] : this.#reflowRetiredHeader(width, 0);
|
|
661
|
+
offered.source.headerRows = headerRows;
|
|
662
|
+
offered.rows = [...headerRows, ...(transcript?.rows ?? [])];
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
#renderRoots(roots: readonly Component[], width: number): string[] {
|
|
666
|
+
const rows: string[] = [];
|
|
667
|
+
for (const root of roots) rows.push(...root.render(width));
|
|
668
|
+
return rows;
|
|
669
|
+
}
|
|
670
|
+
/**
|
|
671
|
+
* Mounted-runtime rows for the transient resize buffer. Only the trailing
|
|
672
|
+
* viewport can survive the caller's bottom slice, so the transcript renders
|
|
673
|
+
* a bounded tail instead of the full committed ledger, and the chrome above
|
|
674
|
+
* it renders only when that tail underfills the screen.
|
|
675
|
+
*/
|
|
676
|
+
#renderResizeTail(width: number, rows: number): string[] {
|
|
677
|
+
const roots = [...this.#runtimeChildren, this.#statusHost];
|
|
678
|
+
const transcriptIndex = roots.findIndex(root => root instanceof TranscriptContainer);
|
|
679
|
+
if (transcriptIndex < 0) return this.#renderRoots(roots, width);
|
|
680
|
+
const transcript = roots[transcriptIndex] as TranscriptContainer;
|
|
681
|
+
const after = this.#renderRoots(roots.slice(transcriptIndex + 1), width);
|
|
682
|
+
const transcriptRows = transcript.renderTail(width, Math.max(0, rows - after.length));
|
|
683
|
+
const pre =
|
|
684
|
+
transcriptRows.length + after.length >= rows ? [] : this.#renderRoots(roots.slice(0, transcriptIndex), width);
|
|
685
|
+
return [...pre, ...transcriptRows, ...after];
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
/** Reflow accepted hard rows exactly as the restored terminal buffer will. */
|
|
689
|
+
#reflowRetiredHeader(width: number, start: number): string[] {
|
|
690
|
+
const lines = this.#retiredHeaderRows;
|
|
691
|
+
if (!lines) return [];
|
|
692
|
+
if (isInsideTerminalMultiplexer()) return lines.slice(start);
|
|
693
|
+
const reflowed: string[] = [];
|
|
694
|
+
const columns = Math.max(1, width);
|
|
695
|
+
for (let index = start; index < lines.length; index++) {
|
|
696
|
+
const line = lines[index]!;
|
|
697
|
+
const lineWidth = visibleWidth(line);
|
|
698
|
+
if (lineWidth === 0) {
|
|
699
|
+
reflowed.push("");
|
|
700
|
+
continue;
|
|
701
|
+
}
|
|
702
|
+
for (let column = 0; column < lineWidth;) {
|
|
703
|
+
let slice = sliceWithWidth(line, column, columns, true);
|
|
704
|
+
if (slice.width === 0) slice = sliceWithWidth(line, column, columns);
|
|
705
|
+
reflowed.push(slice.text);
|
|
706
|
+
column += Math.max(1, slice.width);
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
return reflowed;
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
/** Live editor whose draft survives startup and session adoption. */
|
|
713
|
+
get editor(): CustomEditor {
|
|
714
|
+
return this.#editor;
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
/** The welcome component currently mounted in the header, if quiet mode is off. */
|
|
718
|
+
get welcome(): WelcomeComponent | undefined {
|
|
719
|
+
return this.#welcome;
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
/** Whether this composer already owns the terminal render/input loop. */
|
|
723
|
+
get started(): boolean {
|
|
724
|
+
return this.#started && !this.#stopped;
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
/** Start terminal ownership and optionally begin the welcome intro. */
|
|
728
|
+
start(options: ComposerStartOptions = {}): void {
|
|
729
|
+
if (this.#started || this.#stopped) return;
|
|
730
|
+
this.#started = true;
|
|
731
|
+
this.ui.start({ clearScrollback: options.clearScrollback === true, deferInput: options.deferInput === true });
|
|
732
|
+
if (options.playWelcomeIntro !== false) this.playWelcomeIntro();
|
|
733
|
+
// Deferred input identifies the CLI prepaint handoff. Flush the queued
|
|
734
|
+
// forced frame before returning so subsequent dynamic-import evaluation
|
|
735
|
+
// cannot monopolize the event loop ahead of the speculative status chrome.
|
|
736
|
+
if (options.deferInput === true) this.ui.renderNow({ clearScrollback: options.clearScrollback === true });
|
|
737
|
+
}
|
|
738
|
+
/** Take raw-input ownership after a deferred-input start. Idempotent. */
|
|
739
|
+
enableInput(): void {
|
|
740
|
+
if (this.#stopped) return;
|
|
741
|
+
this.ui.enableInput();
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
/** Apply settings changes without replacing the editor or welcome component. */
|
|
745
|
+
setPreferences(update: Partial<ComposerPreferences>): void {
|
|
746
|
+
if (this.#stopped) return;
|
|
747
|
+
const wasQuiet = this.#preferences.quiet;
|
|
748
|
+
this.#preferences = { ...this.#preferences, ...update };
|
|
749
|
+
this.editor.setTheme(getEditorTheme());
|
|
750
|
+
try {
|
|
751
|
+
this.editor.setBorderStyle(this.#preferences.composerShape);
|
|
752
|
+
} catch {
|
|
753
|
+
// Extension-defined styles arrive with the session; InteractiveMode reapplies them.
|
|
754
|
+
}
|
|
755
|
+
this.ui.setShowHardwareCursor(this.#preferences.showHardwareCursor);
|
|
756
|
+
this.editor.setUseTerminalCursor(this.ui.getShowHardwareCursor());
|
|
757
|
+
this.ui.setMaxInlineImages(this.#preferences.maxInlineImages);
|
|
758
|
+
if (update.resizeScrollback !== undefined) this.ui.setResizeScrollback(update.resizeScrollback);
|
|
759
|
+
this.editor.setImeSafeCursorLayout(this.#preferences.imeSafeCursor);
|
|
760
|
+
this.editor.setAutocompleteMaxVisible(this.#preferences.autocompleteMaxVisible);
|
|
761
|
+
this.editor.setSpellingFeatures({
|
|
762
|
+
typoDetection: this.#preferences.spellingTypoDetection,
|
|
763
|
+
autocomplete: this.#preferences.spellingAutocomplete,
|
|
764
|
+
autocorrect: this.#preferences.spellingAutocorrect,
|
|
765
|
+
});
|
|
766
|
+
this.#applyStatusSnapshot();
|
|
767
|
+
if (this.#preferences.quiet) {
|
|
768
|
+
this.#welcome?.stopIntro();
|
|
769
|
+
this.#welcome = undefined;
|
|
770
|
+
} else {
|
|
771
|
+
this.#ensureWelcome();
|
|
772
|
+
this.#welcome?.invalidate();
|
|
773
|
+
if (wasQuiet && this.#started) this.playWelcomeIntro();
|
|
774
|
+
}
|
|
775
|
+
if (wasQuiet !== this.#preferences.quiet) this.#rebuildHeader();
|
|
776
|
+
this.ui.requestRender();
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
/** Patch welcome data in place as model, session, and project discovery complete. */
|
|
780
|
+
updateWelcome(update: ComposerWelcomeUpdate): void {
|
|
781
|
+
if (this.#stopped) return;
|
|
782
|
+
this.#applyWelcomeUpdate(update);
|
|
783
|
+
if (this.#preferences.quiet) return;
|
|
784
|
+
this.#ensureWelcome();
|
|
785
|
+
const welcome = this.#welcome;
|
|
786
|
+
if (!welcome) return;
|
|
787
|
+
if (update.version !== undefined) welcome.setVersion(this.#version);
|
|
788
|
+
if (update.modelName !== undefined || update.providerName !== undefined) {
|
|
789
|
+
welcome.setModel(this.#modelName, this.#providerName);
|
|
790
|
+
}
|
|
791
|
+
if (update.recentSessions !== undefined) welcome.setRecentSessions(this.#recentSessions);
|
|
792
|
+
if (update.lspServers !== undefined) welcome.setLspServers(this.#lspServers);
|
|
793
|
+
this.ui.requestRender();
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
/** Replace optional header content around the stable welcome scene. */
|
|
797
|
+
setHeaderExtras(before: readonly Component[], after: readonly Component[]): void {
|
|
798
|
+
if (this.#stopped) return;
|
|
799
|
+
this.#headerBefore = before;
|
|
800
|
+
this.#headerAfter = after;
|
|
801
|
+
this.#rebuildHeader();
|
|
802
|
+
this.ui.requestRender();
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
/** Update the canonical editor reference after InteractiveMode remounts a custom editor. */
|
|
806
|
+
setEditor(editor: CustomEditor): void {
|
|
807
|
+
this.#editor = editor;
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
/**
|
|
811
|
+
* Mount the session-aware status component into the slot below the editor.
|
|
812
|
+
* Drops the speculative snapshot; the caller installs the real top-border
|
|
813
|
+
* provider through its composer-shape sync.
|
|
814
|
+
*/
|
|
815
|
+
setStatusComponent(component: Component): void {
|
|
816
|
+
this.#statusHost.setComponent(component);
|
|
817
|
+
this.#statusSnapshot = undefined;
|
|
818
|
+
this.editor.setTopBorderProvider(undefined);
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
/** Cached placeholder top-border content fitted to the current editor width. */
|
|
822
|
+
#speculativeTopBorder(availableWidth: number): EditorTopBorder | undefined {
|
|
823
|
+
const border = this.#statusSnapshot?.topBorder;
|
|
824
|
+
if (!border) return undefined;
|
|
825
|
+
if (border.width <= availableWidth) return { content: border.content, width: border.width };
|
|
826
|
+
const content = truncateToWidth(border.content, availableWidth);
|
|
827
|
+
return { content, width: visibleWidth(content) };
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
/** Install the cached chrome for the current shape; a shape mismatch clears it. */
|
|
831
|
+
#applyStatusSnapshot(): void {
|
|
832
|
+
if (this.#statusHost.mounted) return;
|
|
833
|
+
const snapshot = this.#statusSnapshot;
|
|
834
|
+
if (!snapshot || snapshot.shape !== this.#preferences.composerShape) {
|
|
835
|
+
this.editor.setTopBorderProvider(undefined);
|
|
836
|
+
this.#statusHost.setLines([]);
|
|
837
|
+
return;
|
|
838
|
+
}
|
|
839
|
+
if (snapshot.borderColor) {
|
|
840
|
+
const { prefix, suffix } = snapshot.borderColor;
|
|
841
|
+
this.editor.borderColor = text => `${prefix}${text}${suffix}`;
|
|
842
|
+
}
|
|
843
|
+
this.editor.setTopBorderProvider(
|
|
844
|
+
snapshot.topBorder ? availableWidth => this.#speculativeTopBorder(availableWidth) : undefined,
|
|
845
|
+
);
|
|
846
|
+
this.#statusHost.setLines(snapshot.bottomLines);
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
/** Mount or replace session-aware root children while preserving the header and status hosts. */
|
|
850
|
+
setRuntimeChildren(children: readonly Component[]): void {
|
|
851
|
+
if (this.#stopped) return;
|
|
852
|
+
this.ui.removeChild(this.#statusHost);
|
|
853
|
+
if (this.#runtimeMounted) {
|
|
854
|
+
for (const child of this.#runtimeChildren) this.ui.removeChild(child);
|
|
855
|
+
} else {
|
|
856
|
+
this.ui.removeChild(this.#bootstrapInputGap);
|
|
857
|
+
this.ui.removeChild(this.editor);
|
|
858
|
+
this.#runtimeMounted = true;
|
|
859
|
+
}
|
|
860
|
+
this.#runtimeChildren = children;
|
|
861
|
+
for (const child of children) this.ui.addChild(child);
|
|
862
|
+
this.ui.addChild(this.#statusHost);
|
|
863
|
+
this.ui.requestRender();
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
/** Play or replay the welcome intro against the stable header render target. */
|
|
867
|
+
playWelcomeIntro(): void {
|
|
868
|
+
this.#welcome?.playIntro(() => this.ui.requestComponentRender(this.#header));
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
/** Transfer terminal ownership to InteractiveMode without stopping the composer. */
|
|
872
|
+
transfer(): void {
|
|
873
|
+
if (!this.#started || this.#stopped || this.#transferred) {
|
|
874
|
+
throw new Error("Composer is not available for transfer");
|
|
875
|
+
}
|
|
876
|
+
this.#transferred = true;
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
/** Stop a composer that has not transferred terminal ownership. */
|
|
880
|
+
stop(): void {
|
|
881
|
+
if (!this.#started || this.#stopped || this.#transferred) return;
|
|
882
|
+
this.#welcome?.stopIntro();
|
|
883
|
+
this.ui.stop();
|
|
884
|
+
this.#stopped = true;
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
#applyWelcomeUpdate(update: ComposerWelcomeUpdate): void {
|
|
888
|
+
if (update.version !== undefined) this.#version = update.version;
|
|
889
|
+
if (update.modelName !== undefined) this.#modelName = update.modelName;
|
|
890
|
+
if (update.providerName !== undefined) this.#providerName = update.providerName;
|
|
891
|
+
if (update.recentSessions !== undefined) this.#recentSessions = [...update.recentSessions];
|
|
892
|
+
if (update.lspServers !== undefined) this.#lspServers = [...update.lspServers];
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
#ensureWelcome(): void {
|
|
896
|
+
this.#welcome ??= new WelcomeComponent(
|
|
897
|
+
this.#version,
|
|
898
|
+
this.#modelName,
|
|
899
|
+
this.#providerName,
|
|
900
|
+
this.#recentSessions,
|
|
901
|
+
this.#lspServers,
|
|
902
|
+
);
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
#rebuildHeader(): void {
|
|
906
|
+
this.#header.clear();
|
|
907
|
+
for (const component of this.#headerBefore) this.#header.addChild(component);
|
|
908
|
+
if (this.#welcome) {
|
|
909
|
+
this.#header.addChild(new Spacer(1));
|
|
910
|
+
this.#header.addChild(this.#welcome);
|
|
911
|
+
this.#header.addChild(new Spacer(1));
|
|
912
|
+
}
|
|
913
|
+
for (const component of this.#headerAfter) this.#header.addChild(component);
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
#handleInterrupt(): void {
|
|
917
|
+
const now = this.#now();
|
|
918
|
+
if (now - this.#lastInterruptAt < DOUBLE_INTERRUPT_MS) {
|
|
919
|
+
this.#requestExit(130);
|
|
920
|
+
return;
|
|
921
|
+
}
|
|
922
|
+
this.editor.setText("");
|
|
923
|
+
this.#lastInterruptAt = now;
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
#requestExit(code: number): void {
|
|
927
|
+
// Remains live after transfer until InteractiveMode installs its configured handlers.
|
|
928
|
+
if (this.#stopped) return;
|
|
929
|
+
this.#welcome?.stopIntro();
|
|
930
|
+
if (this.#started) this.ui.stop();
|
|
931
|
+
this.#stopped = true;
|
|
932
|
+
this.#exit(code);
|
|
933
|
+
}
|
|
934
|
+
}
|