@oh-my-pi/pi-tui 18.4.2 → 18.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +56 -0
- package/dist/types/apps/cleanse-board.d.ts +17 -0
- package/dist/types/apps/git/help.d.ts +16 -0
- package/dist/types/apps/git/sidebar.d.ts +18 -0
- package/dist/types/apps/live-visualizer.d.ts +10 -0
- package/dist/types/apps/ps-top.d.ts +15 -0
- package/dist/types/apps/standalone-picker.d.ts +3 -0
- package/dist/types/autocomplete.d.ts +8 -0
- package/dist/types/chat/advisor-message.d.ts +12 -2
- package/dist/types/chat/assistant-message.d.ts +20 -0
- package/dist/types/chat/bash-execution.d.ts +9 -0
- package/dist/types/chat/chat-transcript-builder.d.ts +6 -1
- package/dist/types/chat/collab-prompt-message.d.ts +4 -0
- package/dist/types/chat/compaction-summary-message.d.ts +15 -0
- package/dist/types/chat/eval-execution.d.ts +8 -0
- package/dist/types/chat/execution-shared.d.ts +58 -0
- package/dist/types/chat/late-diagnostics-message.d.ts +15 -1
- package/dist/types/chat/read-tool-group.d.ts +15 -0
- package/dist/types/chat/skill-message.d.ts +9 -0
- package/dist/types/chat/stripped-tool-calls-placeholder.d.ts +3 -0
- package/dist/types/chat/thinking-display.d.ts +2 -0
- package/dist/types/chat/todo-reminder.d.ts +4 -1
- package/dist/types/chat/tool-execution.d.ts +25 -1
- package/dist/types/chat/transcript-actions.d.ts +37 -0
- package/dist/types/chat/transcript-entry.d.ts +3 -1
- package/dist/types/chat/user-message.d.ts +25 -0
- package/dist/types/chrome/collab-qrcode.d.ts +7 -0
- package/dist/types/chrome/context-thresholds.d.ts +11 -0
- package/dist/types/chrome/countdown-timer.d.ts +9 -3
- package/dist/types/chrome/diff.d.ts +12 -0
- package/dist/types/chrome/dynamic-border.d.ts +3 -0
- package/dist/types/chrome/format.d.ts +6 -0
- package/dist/types/chrome/message-divider.d.ts +13 -0
- package/dist/types/chrome/message-frame.d.ts +10 -0
- package/dist/types/chrome/message-notice.d.ts +23 -0
- package/dist/types/chrome/overlay-box.d.ts +11 -2
- package/dist/types/chrome/qrcode.d.ts +7 -0
- package/dist/types/chrome/segment-track.d.ts +7 -0
- package/dist/types/chrome/selector-helpers.d.ts +0 -6
- package/dist/types/chrome/status-notice.d.ts +14 -0
- package/dist/types/chrome/streaming-panel.d.ts +3 -0
- package/dist/types/chrome/tool-activity.d.ts +3 -0
- package/dist/types/chrome/transcript-container.d.ts +13 -0
- package/dist/types/chrome/transcript-status.d.ts +15 -3
- package/dist/types/components/box.d.ts +7 -0
- package/dist/types/components/disclosure.d.ts +9 -0
- package/dist/types/components/editor.d.ts +42 -0
- package/dist/types/components/form.d.ts +14 -0
- package/dist/types/components/image.d.ts +7 -0
- package/dist/types/components/input.d.ts +20 -1
- package/dist/types/components/key-value-list.d.ts +3 -0
- package/dist/types/components/layout/geometry.d.ts +16 -0
- package/dist/types/components/layout/row.d.ts +7 -0
- package/dist/types/components/layout/split-pane.d.ts +8 -0
- package/dist/types/components/layout/stack.d.ts +6 -0
- package/dist/types/components/loader.d.ts +48 -0
- package/dist/types/components/markdown.d.ts +8 -0
- package/dist/types/components/metric.d.ts +7 -0
- package/dist/types/components/progress-bar.d.ts +3 -0
- package/dist/types/components/scroll-view.d.ts +6 -0
- package/dist/types/components/section.d.ts +3 -0
- package/dist/types/components/select-list.d.ts +33 -0
- package/dist/types/components/settings-list.d.ts +68 -0
- package/dist/types/components/spacer.d.ts +4 -0
- package/dist/types/components/tab-bar.d.ts +9 -0
- package/dist/types/components/table.d.ts +7 -0
- package/dist/types/components/text.d.ts +6 -0
- package/dist/types/components/tree-view.d.ts +9 -0
- package/dist/types/components/truncated-text.d.ts +3 -0
- package/dist/types/components/wizard-step.d.ts +6 -0
- package/dist/types/hotkeys-markdown.d.ts +24 -0
- package/dist/types/index.d.ts +5 -0
- package/dist/types/latex-block.d.ts +7 -0
- package/dist/types/loop-watchdog.d.ts +9 -0
- package/dist/types/native/apply.d.ts +39 -0
- package/dist/types/native/backend.d.ts +94 -0
- package/dist/types/native/blobs.d.ts +29 -0
- package/dist/types/native/describe.d.ts +56 -0
- package/dist/types/native/encode.d.ts +50 -0
- package/dist/types/native/icons.d.ts +18 -0
- package/dist/types/native/memo.d.ts +27 -0
- package/dist/types/native/node.d.ts +107 -0
- package/dist/types/native/overlay.d.ts +64 -0
- package/dist/types/native/picker.d.ts +124 -0
- package/dist/types/native/reconcile.d.ts +41 -0
- package/dist/types/native/settle.d.ts +11 -0
- package/dist/types/native/spans.d.ts +21 -0
- package/dist/types/native/state.d.ts +6 -0
- package/dist/types/native/tone.d.ts +24 -0
- package/dist/types/overlays/advisor-config.d.ts +20 -0
- package/dist/types/overlays/agent-hub-renderer.d.ts +27 -11
- package/dist/types/overlays/agent-hub.d.ts +10 -0
- package/dist/types/overlays/agent-transcript-viewer.d.ts +7 -0
- package/dist/types/overlays/agents-hub.d.ts +8 -0
- package/dist/types/overlays/annotation-overlay.d.ts +3 -0
- package/dist/types/overlays/ask-dialog.d.ts +23 -0
- package/dist/types/overlays/bordered-loader.d.ts +3 -0
- package/dist/types/overlays/btw-history-panel.d.ts +9 -0
- package/dist/types/overlays/btw-panel.d.ts +3 -0
- package/dist/types/overlays/cleanse-panel.d.ts +5 -2
- package/dist/types/overlays/composer-shape-preview.d.ts +7 -1
- package/dist/types/overlays/copy-selector.d.ts +38 -0
- package/dist/types/overlays/error-banner.d.ts +9 -0
- package/dist/types/overlays/extensions/extension-list.d.ts +1 -0
- package/dist/types/overlays/history-search.d.ts +18 -0
- package/dist/types/overlays/hook-editor.d.ts +28 -1
- package/dist/types/overlays/hook-input.d.ts +3 -0
- package/dist/types/overlays/hook-selector.d.ts +10 -0
- package/dist/types/overlays/hub-frame.d.ts +18 -0
- package/dist/types/overlays/jobs-panel.d.ts +24 -0
- package/dist/types/overlays/login-dialog.d.ts +10 -0
- package/dist/types/overlays/logout-account-selector.d.ts +4 -0
- package/dist/types/overlays/mcp-add-wizard.d.ts +4 -0
- package/dist/types/overlays/model-browser.d.ts +69 -0
- package/dist/types/overlays/model-hub.d.ts +7 -0
- package/dist/types/overlays/model-picker.d.ts +9 -1
- package/dist/types/overlays/move-overlay.d.ts +3 -0
- package/dist/types/overlays/oauth-selector.d.ts +4 -0
- package/dist/types/overlays/omfg-panel.d.ts +3 -0
- package/dist/types/overlays/pause-screen.d.ts +12 -0
- package/dist/types/overlays/plan-review-overlay.d.ts +5 -0
- package/dist/types/overlays/plan-save-overlay.d.ts +10 -0
- package/dist/types/overlays/plugin-selector.d.ts +4 -0
- package/dist/types/overlays/plugin-settings.d.ts +30 -0
- package/dist/types/overlays/queue-mode-selector.d.ts +4 -0
- package/dist/types/overlays/reset-usage-selector.d.ts +8 -0
- package/dist/types/overlays/rewind-selector.d.ts +5 -0
- package/dist/types/overlays/session-info-overlay.d.ts +19 -1
- package/dist/types/overlays/session-selector.d.ts +64 -1
- package/dist/types/overlays/settings-defs.d.ts +14 -0
- package/dist/types/overlays/settings-selector.d.ts +21 -0
- package/dist/types/overlays/show-images-selector.d.ts +4 -0
- package/dist/types/overlays/snapcompact-shape-preview.d.ts +2 -0
- package/dist/types/overlays/stats-notice.d.ts +8 -0
- package/dist/types/overlays/theme-selector.d.ts +11 -0
- package/dist/types/overlays/thinking-selector.d.ts +4 -0
- package/dist/types/overlays/tree-selector.d.ts +60 -2
- package/dist/types/overlays/usage-dashboard.d.ts +26 -3
- package/dist/types/overlays/usage-row.d.ts +36 -2
- package/dist/types/prompt/attachment-chips.d.ts +8 -0
- package/dist/types/prompt/composer-attachments.d.ts +15 -3
- package/dist/types/prompt/composer.d.ts +18 -1
- package/dist/types/prompt/custom-editor.d.ts +48 -2
- package/dist/types/prompt/editor-top-gap.d.ts +2 -0
- package/dist/types/prompt/macos-spelling.d.ts +6 -0
- package/dist/types/prompt/magic-keywords.d.ts +9 -0
- package/dist/types/prompt/queued-messages.d.ts +23 -0
- package/dist/types/prompt/skill-tokens.d.ts +4 -2
- package/dist/types/prompt/welcome.d.ts +16 -0
- package/dist/types/render/code-cell.d.ts +10 -0
- package/dist/types/render/file-list.d.ts +8 -0
- package/dist/types/render/hyperlink.d.ts +14 -0
- package/dist/types/render/output-block.d.ts +43 -0
- package/dist/types/render/output-pane.d.ts +17 -0
- package/dist/types/render/tool-card.d.ts +8 -0
- package/dist/types/render/tree-list.d.ts +8 -0
- package/dist/types/render/width-aware-text.d.ts +7 -0
- package/dist/types/setup/scenes/outro.d.ts +7 -0
- package/dist/types/setup/scenes/sign-in.d.ts +16 -9
- package/dist/types/setup/scenes/splash.d.ts +8 -0
- package/dist/types/setup/scenes/types.d.ts +6 -27
- package/dist/types/setup/wizard-overlay.d.ts +9 -0
- package/dist/types/status-line/component.d.ts +49 -5
- package/dist/types/status-line/context-usage.d.ts +20 -0
- package/dist/types/status-line/footer.d.ts +8 -0
- package/dist/types/status-line/metrics.d.ts +1 -2
- package/dist/types/status-line/segments.d.ts +10 -1
- package/dist/types/status-line/types.d.ts +40 -1
- package/dist/types/stdin-buffer.d.ts +10 -7
- package/dist/types/terminal-capabilities.d.ts +1 -1
- package/dist/types/terminal.d.ts +36 -0
- package/dist/types/theme/defaults/index.d.ts +100 -0
- package/dist/types/theme/schema.d.ts +2 -0
- package/dist/types/theme/shimmer.d.ts +8 -0
- package/dist/types/theme/theme-class.d.ts +2 -0
- package/dist/types/theme/theme.d.ts +33 -1
- package/dist/types/tools/ask.d.ts +3 -0
- package/dist/types/tools/ast-edit.d.ts +10 -1
- package/dist/types/tools/ast-grep.d.ts +10 -1
- package/dist/types/tools/autoresearch.d.ts +21 -4
- package/dist/types/tools/bash.d.ts +30 -1
- package/dist/types/tools/cfg-render.d.ts +5 -1
- package/dist/types/tools/debug.d.ts +3 -1
- package/dist/types/tools/default-renderer.d.ts +6 -1
- package/dist/types/tools/edit.d.ts +14 -1
- package/dist/types/tools/eval.d.ts +12 -1
- package/dist/types/tools/fetch.d.ts +16 -1
- package/dist/types/tools/find.d.ts +15 -1
- package/dist/types/tools/github.d.ts +3 -0
- package/dist/types/tools/glob.d.ts +14 -1
- package/dist/types/tools/goal.d.ts +3 -1
- package/dist/types/tools/grep.d.ts +14 -1
- package/dist/types/tools/grouped-file-output.d.ts +58 -0
- package/dist/types/tools/json-tree.d.ts +20 -0
- package/dist/types/tools/lsp.d.ts +3 -10
- package/dist/types/tools/mcp.d.ts +16 -1
- package/dist/types/tools/memory.d.ts +26 -0
- package/dist/types/tools/native-view.d.ts +90 -0
- package/dist/types/tools/proc-render.d.ts +7 -1
- package/dist/types/tools/read.d.ts +23 -1
- package/dist/types/tools/renderer.d.ts +57 -0
- package/dist/types/tools/report-tool-issue.d.ts +3 -0
- package/dist/types/tools/resolve.d.ts +14 -1
- package/dist/types/tools/task.d.ts +3 -0
- package/dist/types/tools/think.d.ts +3 -0
- package/dist/types/tools/todo.d.ts +12 -0
- package/dist/types/tools/vibe.d.ts +3 -1
- package/dist/types/tools/wait.d.ts +3 -0
- package/dist/types/tools/web-search-types.d.ts +90 -0
- package/dist/types/tools/web-search.d.ts +9 -168
- package/dist/types/tools/write.d.ts +14 -1
- package/dist/types/tools/xdev.d.ts +13 -0
- package/dist/types/tui.d.ts +49 -0
- package/package.json +9 -9
- package/src/apps/autoresearch-dashboard.ts +362 -4
- package/src/apps/cleanse-board.ts +219 -1
- package/src/apps/cleanse-picker.ts +9 -1
- package/src/apps/debug/log-viewer.ts +15 -15
- package/src/apps/debug/protocol-probe.ts +2 -20
- package/src/apps/git/avatar.ts +2 -22
- package/src/apps/git/git-tui.ts +406 -59
- package/src/apps/git/help.ts +130 -0
- package/src/apps/git/sidebar.ts +491 -0
- package/src/apps/live-visualizer.ts +65 -0
- package/src/apps/ps-top.ts +448 -17
- package/src/apps/session-picker.ts +1 -0
- package/src/apps/setup-model-picker.ts +5 -1
- package/src/apps/standalone-picker.ts +55 -1
- package/src/autocomplete.ts +44 -1
- package/src/chat/advisor-message.ts +72 -3
- package/src/chat/assistant-message.ts +538 -51
- package/src/chat/background-tan-message.ts +4 -4
- package/src/chat/bash-execution.ts +81 -0
- package/src/chat/cache-invalidation-marker.ts +8 -0
- package/src/chat/chat-transcript-builder.ts +37 -6
- package/src/chat/collab-prompt-message.ts +15 -3
- package/src/chat/compaction-summary-message.ts +74 -1
- package/src/chat/custom-message.ts +3 -1
- package/src/chat/eval-execution.ts +57 -0
- package/src/chat/execution-shared.ts +153 -1
- package/src/chat/hook-message.ts +1 -0
- package/src/chat/late-diagnostics-message.ts +85 -2
- package/src/chat/read-tool-group.ts +341 -1
- package/src/chat/served-model-marker.ts +8 -0
- package/src/chat/skill-message.ts +63 -0
- package/src/chat/stripped-tool-calls-placeholder.ts +18 -10
- package/src/chat/thinking-display.ts +73 -11
- package/src/chat/todo-reminder.ts +34 -4
- package/src/chat/tool-execution.ts +385 -10
- package/src/chat/transcript-actions.ts +38 -0
- package/src/chat/transcript-browser.ts +2 -9
- package/src/chat/transcript-entry.ts +10 -1
- package/src/chat/transcript-render-helpers.ts +16 -15
- package/src/chat/ttsr-notification.ts +38 -1
- package/src/chat/user-message.ts +167 -4
- package/src/chrome/collab-qrcode.ts +27 -2
- package/src/chrome/context-thresholds.ts +34 -0
- package/src/chrome/countdown-timer.ts +24 -1
- package/src/chrome/diff.ts +60 -1
- package/src/chrome/dynamic-border.ts +7 -0
- package/src/chrome/format.ts +12 -0
- package/src/chrome/message-divider.ts +33 -1
- package/src/chrome/message-frame.ts +53 -10
- package/src/chrome/message-notice.ts +71 -0
- package/src/chrome/overlay-box.ts +45 -2
- package/src/chrome/qrcode.ts +28 -0
- package/src/chrome/segment-track.ts +26 -0
- package/src/chrome/selector-helpers.ts +1 -17
- package/src/chrome/status-notice.ts +46 -0
- package/src/chrome/streaming-panel.ts +25 -1
- package/src/chrome/tool-activity.ts +20 -0
- package/src/chrome/transcript-container.ts +139 -17
- package/src/chrome/transcript-status.ts +40 -11
- package/src/components/box.ts +51 -0
- package/src/components/disclosure.ts +33 -0
- package/src/components/editor.ts +239 -36
- package/src/components/form.ts +93 -9
- package/src/components/image.ts +30 -0
- package/src/components/input.ts +77 -26
- package/src/components/key-value-list.ts +18 -0
- package/src/components/layout/geometry.ts +44 -0
- package/src/components/layout/row.ts +30 -0
- package/src/components/layout/split-pane.ts +65 -0
- package/src/components/layout/stack.ts +27 -0
- package/src/components/loader.ts +183 -1
- package/src/components/markdown.ts +131 -56
- package/src/components/metric.ts +39 -0
- package/src/components/progress-bar.ts +28 -0
- package/src/components/scroll-view.ts +23 -0
- package/src/components/section.ts +16 -0
- package/src/components/select-list.ts +174 -25
- package/src/components/settings-list.ts +264 -7
- package/src/components/spacer.ts +17 -0
- package/src/components/tab-bar.ts +42 -0
- package/src/components/table.ts +34 -0
- package/src/components/text.ts +23 -0
- package/src/components/tree-view.ts +65 -0
- package/src/components/truncated-text.ts +14 -0
- package/src/components/wizard-step.ts +18 -9
- package/src/debug-server.ts +14 -7
- package/src/hotkeys-markdown.ts +251 -69
- package/src/index.ts +6 -0
- package/src/latex-block.ts +11 -0
- package/src/loop-watchdog.ts +38 -10
- package/src/native/apply.ts +275 -0
- package/src/native/backend.ts +682 -0
- package/src/native/blobs.ts +70 -0
- package/src/native/describe.ts +124 -0
- package/src/native/encode.ts +233 -0
- package/src/native/icons.ts +184 -0
- package/src/native/memo.ts +64 -0
- package/src/native/node.ts +112 -0
- package/src/native/overlay.ts +131 -0
- package/src/native/picker.ts +333 -0
- package/src/native/reconcile.ts +1063 -0
- package/src/native/settle.ts +23 -0
- package/src/native/spans.ts +308 -0
- package/src/native/state.ts +28 -0
- package/src/native/tone.ts +79 -0
- package/src/overlays/advisor-config.ts +420 -0
- package/src/overlays/agent-hub-renderer.ts +155 -18
- package/src/overlays/agent-hub.ts +1037 -44
- package/src/overlays/agent-transcript-viewer.ts +129 -0
- package/src/overlays/agents-hub.ts +688 -53
- package/src/overlays/annotation-overlay.ts +391 -52
- package/src/overlays/ask-dialog.ts +435 -24
- package/src/overlays/bordered-loader.ts +17 -0
- package/src/overlays/btw-history-panel.ts +182 -0
- package/src/overlays/btw-panel.ts +68 -2
- package/src/overlays/cleanse-panel.ts +105 -15
- package/src/overlays/codex-reset-fireworks.ts +65 -5
- package/src/overlays/composer-shape-preview.ts +26 -1
- package/src/overlays/copy-selector.ts +545 -23
- package/src/overlays/error-banner.ts +44 -0
- package/src/overlays/extensions/extension-list.ts +23 -17
- package/src/overlays/history-search.ts +175 -33
- package/src/overlays/hook-editor.ts +113 -6
- package/src/overlays/hook-input.ts +22 -1
- package/src/overlays/hook-selector.ts +311 -22
- package/src/overlays/hub-frame.ts +80 -0
- package/src/overlays/jobs-panel.ts +112 -0
- package/src/overlays/login-dialog.ts +183 -1
- package/src/overlays/logout-account-selector.ts +102 -4
- package/src/overlays/mcp-add-wizard.ts +232 -53
- package/src/overlays/model-browser.ts +681 -22
- package/src/overlays/model-hub.ts +1402 -78
- package/src/overlays/model-picker.ts +184 -7
- package/src/overlays/move-overlay.ts +45 -0
- package/src/overlays/oauth-selector.ts +227 -23
- package/src/overlays/omfg-panel.ts +86 -2
- package/src/overlays/pause-screen.ts +55 -4
- package/src/overlays/plan-review-overlay.ts +352 -0
- package/src/overlays/plan-save-overlay.ts +45 -0
- package/src/overlays/plugin-selector.ts +21 -1
- package/src/overlays/plugin-settings.ts +195 -5
- package/src/overlays/queue-mode-selector.ts +19 -1
- package/src/overlays/reset-usage-selector.ts +110 -40
- package/src/overlays/rewind-selector.ts +458 -44
- package/src/overlays/session-account-selector.ts +1 -1
- package/src/overlays/session-info-overlay.ts +137 -1
- package/src/overlays/session-selector.ts +807 -46
- package/src/overlays/settings-defs.ts +41 -0
- package/src/overlays/settings-selector.ts +585 -33
- package/src/overlays/show-images-selector.ts +18 -1
- package/src/overlays/snapcompact-shape-preview.ts +48 -3
- package/src/overlays/stats-notice.ts +31 -0
- package/src/overlays/theme-selector.ts +31 -1
- package/src/overlays/thinking-selector.ts +22 -1
- package/src/overlays/tree-selector.ts +570 -110
- package/src/overlays/usage-dashboard.ts +587 -12
- package/src/overlays/usage-row.ts +161 -25
- package/src/prompt/attachment-chips.ts +60 -0
- package/src/prompt/composer-attachments.ts +31 -9
- package/src/prompt/composer.ts +55 -2
- package/src/prompt/custom-editor.ts +302 -4
- package/src/prompt/editor-top-gap.ts +8 -0
- package/src/prompt/macos-spelling.ts +24 -9
- package/src/prompt/magic-keywords.ts +22 -1
- package/src/prompt/model-mention-autocomplete.ts +1 -0
- package/src/prompt/queued-messages.ts +101 -0
- package/src/prompt/skill-tokens.ts +5 -3
- package/src/prompt/welcome.ts +213 -6
- package/src/render/code-cell.ts +103 -2
- package/src/render/file-list.ts +29 -0
- package/src/render/hyperlink.ts +32 -0
- package/src/render/output-block.ts +110 -0
- package/src/render/output-pane.ts +37 -0
- package/src/render/tool-card.ts +137 -0
- package/src/render/tree-list.ts +39 -0
- package/src/render/width-aware-text.ts +20 -0
- package/src/setup/scenes/composer.ts +29 -0
- package/src/setup/scenes/glyph.ts +15 -0
- package/src/setup/scenes/model.ts +36 -8
- package/src/setup/scenes/outro.ts +28 -1
- package/src/setup/scenes/sign-in.ts +138 -24
- package/src/setup/scenes/splash.ts +28 -1
- package/src/setup/scenes/theme.ts +88 -3
- package/src/setup/scenes/types.ts +7 -28
- package/src/setup/startup-splash.ts +46 -9
- package/src/setup/wizard-overlay.ts +140 -23
- package/src/setup/wizard.ts +1 -1
- package/src/status-line/component.ts +414 -18
- package/src/status-line/context-usage.ts +279 -50
- package/src/status-line/footer.ts +94 -1
- package/src/status-line/metrics.ts +3 -6
- package/src/status-line/segments.ts +463 -97
- package/src/status-line/separators.ts +3 -16
- package/src/status-line/types.ts +43 -1
- package/src/stdin-buffer.ts +27 -21
- package/src/terminal-capabilities.ts +7 -0
- package/src/terminal.ts +153 -11
- package/src/theme/defaults/dark-neon-noir.json +100 -0
- package/src/theme/defaults/index.ts +2 -0
- package/src/theme/defaults/light-prism.json +5 -5
- package/src/theme/defaults/onyx.json +62 -62
- package/src/theme/mermaid-cache.ts +10 -1
- package/src/theme/schema.ts +15 -0
- package/src/theme/shimmer.ts +33 -0
- package/src/theme/theme-class.ts +24 -19
- package/src/theme/theme.ts +111 -9
- package/src/theme/tui-adapters.ts +14 -1
- package/src/tools/ask.ts +157 -1
- package/src/tools/ast-edit.ts +174 -1
- package/src/tools/ast-grep.ts +82 -2
- package/src/tools/autoresearch.ts +149 -1
- package/src/tools/bash.ts +233 -62
- package/src/tools/cfg-render.ts +66 -1
- package/src/tools/debug.ts +78 -1
- package/src/tools/default-renderer.ts +35 -1
- package/src/tools/edit.ts +357 -31
- package/src/tools/eval.ts +257 -1
- package/src/tools/fetch.ts +102 -3
- package/src/tools/find.ts +127 -1
- package/src/tools/github.ts +119 -10
- package/src/tools/glob.ts +116 -1
- package/src/tools/goal.ts +56 -1
- package/src/tools/grep.ts +83 -2
- package/src/tools/grouped-file-output.ts +205 -0
- package/src/tools/json-tree.ts +116 -1
- package/src/tools/lsp.ts +318 -1
- package/src/tools/mcp.ts +80 -1
- package/src/tools/memory.ts +93 -11
- package/src/tools/native-view.ts +292 -0
- package/src/tools/proc-render.ts +221 -1
- package/src/tools/read.ts +199 -4
- package/src/tools/renderer.ts +61 -0
- package/src/tools/report-tool-issue.ts +7 -1
- package/src/tools/resolve.ts +63 -1
- package/src/tools/streaming-output.ts +79 -16
- package/src/tools/task.ts +406 -4
- package/src/tools/think.ts +19 -1
- package/src/tools/todo.ts +89 -0
- package/src/tools/vibe.ts +230 -2
- package/src/tools/wait.ts +181 -1
- package/src/tools/web-search-types.ts +99 -0
- package/src/tools/web-search.ts +111 -170
- package/src/tools/write.ts +144 -5
- package/src/tools/xdev.ts +64 -1
- package/src/tui.ts +295 -4
- package/dist/types/setup/scenes/providers.d.ts +0 -3
- package/dist/types/setup/scenes/web-search.d.ts +0 -22
- package/src/setup/scenes/providers.ts +0 -110
- package/src/setup/scenes/web-search.ts +0 -165
|
@@ -15,13 +15,16 @@
|
|
|
15
15
|
* mouse selection nor cmd-click.
|
|
16
16
|
*/
|
|
17
17
|
import type { AgentTool } from "@oh-my-pi/pi-agent-core";
|
|
18
|
+
import type { TspPickerColumn, TspPickerItem, TspPickerProps, TspText } from "@oh-my-pi/pi-wire";
|
|
18
19
|
import { type Component, matchesKey, routeSgrMouseInput, type TUI, truncateToWidth, visibleWidth } from "../index";
|
|
19
20
|
import type { MessageRenderer } from "../chat/extension-types";
|
|
20
21
|
import {
|
|
21
22
|
recentTranscriptEntries,
|
|
22
23
|
type SessionMessageEntryLike as SessionMessageEntry,
|
|
23
24
|
type TranscriptEntryLike as TranscriptEntry,
|
|
25
|
+
textContent,
|
|
24
26
|
transcriptEntryMessage,
|
|
27
|
+
userMessageLabel,
|
|
25
28
|
userTurnDraft,
|
|
26
29
|
} from "../chat/transcript-entry";
|
|
27
30
|
import { expandKeyHint, replaceTabs } from "../render/render-utils";
|
|
@@ -39,6 +42,228 @@ import {
|
|
|
39
42
|
type OutlineTarget,
|
|
40
43
|
outlineRows,
|
|
41
44
|
} from "../chat/transcript-outline";
|
|
45
|
+
import { code, compact, md, node, row, span, text } from "../native/describe";
|
|
46
|
+
import type { DescribeContext, NativeChild, NativeNode, NativeUiEvent } from "../native/node";
|
|
47
|
+
import { actionHint, hintsRow, type NativeHint, overlayCard } from "../native/overlay";
|
|
48
|
+
import { CLOSE_ACTION, type PickerEvent, picker, pickerAction, pickerEvent } from "../native/picker";
|
|
49
|
+
|
|
50
|
+
/** Key of the leading list item that replays the history older than the startup tail. */
|
|
51
|
+
export const EARLIER_TURNS_KEY = "earlier";
|
|
52
|
+
|
|
53
|
+
/** Leading item of a truncated transcript list; selecting it loads the older turns. */
|
|
54
|
+
export const earlierTurnsItem: NativeNode = node(
|
|
55
|
+
"item",
|
|
56
|
+
{ label: [span("Earlier turns…", "muted")], hint: ["a"] },
|
|
57
|
+
undefined,
|
|
58
|
+
EARLIER_TURNS_KEY,
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
/** First non-blank line of `source`, whitespace collapsed. */
|
|
62
|
+
function firstLine(source: string): string {
|
|
63
|
+
const line = source.split("\n").find(part => part.trim().length > 0) ?? "";
|
|
64
|
+
return line.replace(/\s+/g, " ").trim();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Plain one-line label and styling role for the turn a target opens. */
|
|
68
|
+
function turnSummary(entry: TranscriptEntry): { label: string; role: string } {
|
|
69
|
+
const message = transcriptEntryMessage(entry);
|
|
70
|
+
switch (message?.role) {
|
|
71
|
+
case "user":
|
|
72
|
+
return { label: userMessageLabel(message.content), role: "omp.user" };
|
|
73
|
+
case "assistant": {
|
|
74
|
+
let prose = "";
|
|
75
|
+
const tools: string[] = [];
|
|
76
|
+
for (const content of message.content) {
|
|
77
|
+
if (content.type === "text") prose += content.text;
|
|
78
|
+
else if (content.type === "toolCall") tools.push(content.name);
|
|
79
|
+
}
|
|
80
|
+
const label = firstLine(prose) || tools.join(", ") || "thinking";
|
|
81
|
+
return { label, role: "omp.assistant" };
|
|
82
|
+
}
|
|
83
|
+
case "toolResult":
|
|
84
|
+
return { label: `${message.toolName} result`, role: `omp.tool.${message.toolName}` };
|
|
85
|
+
case "bashExecution":
|
|
86
|
+
return { label: `$ ${firstLine(message.command)}`, role: "omp.tool.bash" };
|
|
87
|
+
case "pythonExecution":
|
|
88
|
+
return { label: firstLine(message.code), role: "omp.tool.eval" };
|
|
89
|
+
case "compactionSummary":
|
|
90
|
+
return { label: "Compaction summary", role: "omp.summary" };
|
|
91
|
+
case "branchSummary":
|
|
92
|
+
return { label: "Branch summary", role: "omp.summary" };
|
|
93
|
+
case "custom":
|
|
94
|
+
case "hookMessage": {
|
|
95
|
+
const draft = userTurnDraft(entry);
|
|
96
|
+
if (draft !== undefined) return { label: firstLine(draft), role: "omp.user" };
|
|
97
|
+
return { label: firstLine(textContent(message.content, " ")) || message.customType, role: "omp.custom" };
|
|
98
|
+
}
|
|
99
|
+
default:
|
|
100
|
+
return { label: entry.id, role: "omp.message" };
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** Local `HH:MM` of an entry's persisted timestamp; empty when unparsable. */
|
|
105
|
+
function entryTime(entry: TranscriptEntry): string {
|
|
106
|
+
const date = new Date(entry.timestamp);
|
|
107
|
+
if (Number.isNaN(date.getTime())) return "";
|
|
108
|
+
return `${String(date.getHours()).padStart(2, "0")}:${String(date.getMinutes()).padStart(2, "0")}`;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* One list item per outline target, keyed by the turn's opening entry id:
|
|
113
|
+
* the plain turn summary as label, `detail` (or the entry time) on the right.
|
|
114
|
+
*/
|
|
115
|
+
export function turnItem(target: OutlineTarget, detail?: TspText): NativeNode {
|
|
116
|
+
const entry = target.entries[0]!;
|
|
117
|
+
const { label, role } = turnSummary(entry);
|
|
118
|
+
const time = entryTime(entry);
|
|
119
|
+
return node(
|
|
120
|
+
"item",
|
|
121
|
+
{
|
|
122
|
+
label: label || "(empty)",
|
|
123
|
+
detail: detail ?? (time || undefined),
|
|
124
|
+
value: detail !== undefined && time ? [span(time, "dim")] : undefined,
|
|
125
|
+
role,
|
|
126
|
+
tone: target.isUserTurn ? "user" : undefined,
|
|
127
|
+
},
|
|
128
|
+
undefined,
|
|
129
|
+
target.turnId,
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** Preview of a turn's text: prose as markdown, command/tool output as a plain code block. */
|
|
134
|
+
export function turnPreview(target: OutlineTarget, content: string): NativeNode {
|
|
135
|
+
if (!content.trim()) return text([span("(no text)", "muted")]);
|
|
136
|
+
const message = transcriptEntryMessage(target.entries[0]!);
|
|
137
|
+
switch (message?.role) {
|
|
138
|
+
case "assistant":
|
|
139
|
+
// A pure tool turn previews its joined commands/results, not prose.
|
|
140
|
+
return message.content.some(part => part.type === "text" && part.text.trim())
|
|
141
|
+
? md(content)
|
|
142
|
+
: code(content, { wrap: true });
|
|
143
|
+
case "user":
|
|
144
|
+
case "custom":
|
|
145
|
+
case "hookMessage":
|
|
146
|
+
case "compactionSummary":
|
|
147
|
+
case "branchSummary":
|
|
148
|
+
return md(content);
|
|
149
|
+
default:
|
|
150
|
+
return code(content, { wrap: true });
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/** Argument keys naming what a tool call works on, in preference order. */
|
|
155
|
+
const TOOL_TARGET_KEYS = ["command", "pattern", "path", "file_path", "query", "url", "code"] as const;
|
|
156
|
+
|
|
157
|
+
/** One-line label of a tool call: its name and main argument (`grep ack`, `bash ls -la`). */
|
|
158
|
+
function toolCallLabel(name: string, args: Record<string, unknown> | undefined): string {
|
|
159
|
+
for (const key of TOOL_TARGET_KEYS) {
|
|
160
|
+
const value = args?.[key];
|
|
161
|
+
if (typeof value === "string" && value.trim()) return `${name} ${firstLine(value)}`;
|
|
162
|
+
}
|
|
163
|
+
return name;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/** The timeline pickers' one fact column: a user turn's clock time. */
|
|
167
|
+
export const TIMELINE_COLUMNS: readonly TspPickerColumn[] = [{ id: "at", format: "dim", priority: 1 }];
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* The `timeline` picker item of one outline target (rewind, copy), keyed by
|
|
171
|
+
* the turn's opening entry id: user turns carry their `HH:MM`, pure tool turns
|
|
172
|
+
* the tool's role and a `name target` label.
|
|
173
|
+
*/
|
|
174
|
+
export function timelineItem(target: OutlineTarget): TspPickerItem {
|
|
175
|
+
const entry = target.entries[0]!;
|
|
176
|
+
const id = target.turnId;
|
|
177
|
+
const summary = turnSummary(entry);
|
|
178
|
+
const label = summary.label || "(empty)";
|
|
179
|
+
if (target.isUserTurn) {
|
|
180
|
+
const at = entryTime(entry);
|
|
181
|
+
return { id, label, node: "user", ...(at ? { facts: { at } } : {}) };
|
|
182
|
+
}
|
|
183
|
+
const message = transcriptEntryMessage(entry);
|
|
184
|
+
switch (message?.role) {
|
|
185
|
+
case "assistant": {
|
|
186
|
+
const prose = message.content.some(part => part.type === "text" && part.text.trim());
|
|
187
|
+
const calls = message.content.filter(part => part.type === "toolCall");
|
|
188
|
+
if (prose || calls.length === 0) return { id, label, node: "assistant" };
|
|
189
|
+
return {
|
|
190
|
+
id,
|
|
191
|
+
label: calls.map(call => toolCallLabel(call.name, call.arguments)).join(" · "),
|
|
192
|
+
node: "tool",
|
|
193
|
+
role: `omp.tool.${calls[0]!.name}`,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
case "toolResult":
|
|
197
|
+
case "bashExecution":
|
|
198
|
+
case "pythonExecution":
|
|
199
|
+
return { id, label, node: "tool", role: summary.role };
|
|
200
|
+
default:
|
|
201
|
+
return { id, label, node: "marker" };
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/** Timeline picker items for `targets`, reused while `targets` is the same array. */
|
|
206
|
+
export class TimelineItems {
|
|
207
|
+
#memo: { targets: readonly OutlineTarget[]; items: TspPickerItem[] } | undefined;
|
|
208
|
+
|
|
209
|
+
of(targets: readonly OutlineTarget[]): TspPickerItem[] {
|
|
210
|
+
if (this.#memo?.targets !== targets) this.#memo = { targets, items: targets.map(timelineItem) };
|
|
211
|
+
return this.#memo.items;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/** Lines of a copy block shown in the native preview; longer blocks copy through omp. */
|
|
216
|
+
const PREVIEW_BLOCK_LINES = 400;
|
|
217
|
+
|
|
218
|
+
/** A block's preview caption: `rust · 12 lines`, `quote`, `link · docs`. */
|
|
219
|
+
function blockCaption(block: CopyBlock): string {
|
|
220
|
+
if (block.href || block.kind === "quote") return block.label;
|
|
221
|
+
const lines = block.content.split("\n").length;
|
|
222
|
+
const kind = block.kind === "code" ? (block.language ?? "code") : block.label;
|
|
223
|
+
return `${kind}${theme.sep.dot}${lines} line${lines === 1 ? "" : "s"}`;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/** A block's preview content: the link, the quote as markdown, else highlighted code. */
|
|
227
|
+
function blockBody(block: CopyBlock): NativeNode {
|
|
228
|
+
if (block.href) return text([span(block.href, "link")], { wrap: "char", href: block.href });
|
|
229
|
+
if (block.kind === "quote") return md(block.content);
|
|
230
|
+
const lines = block.content.split("\n");
|
|
231
|
+
if (lines.length <= PREVIEW_BLOCK_LINES) return code(block.content, { lang: block.language, wrap: true });
|
|
232
|
+
const shown = lines.slice(0, PREVIEW_BLOCK_LINES).join("\n");
|
|
233
|
+
return node("col", { gap: "xs" }, [
|
|
234
|
+
code(shown, { lang: block.language, wrap: true }),
|
|
235
|
+
text([span(`… +${lines.length - PREVIEW_BLOCK_LINES} more lines`, "dim")]),
|
|
236
|
+
]);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* One borderless preview section of the copy picker. A click copies it in the
|
|
241
|
+
* terminal (`copy`) when the preview holds the whole text, else asks omp to
|
|
242
|
+
* (`pick`); link sections also offer `open`. The focused block's section takes
|
|
243
|
+
* the `omp.picker.block.focused` role and the accent tone.
|
|
244
|
+
*/
|
|
245
|
+
function previewSection(
|
|
246
|
+
key: string,
|
|
247
|
+
caption: string,
|
|
248
|
+
body: NativeNode,
|
|
249
|
+
content: string,
|
|
250
|
+
focused: boolean,
|
|
251
|
+
href?: string,
|
|
252
|
+
): NativeNode {
|
|
253
|
+
const whole = content.split("\n").length <= PREVIEW_BLOCK_LINES;
|
|
254
|
+
return node(
|
|
255
|
+
"section",
|
|
256
|
+
{
|
|
257
|
+
head: caption,
|
|
258
|
+
role: focused ? "omp.picker.block.focused" : "omp.picker.block",
|
|
259
|
+
...(focused ? { tone: "accent" as const } : {}),
|
|
260
|
+
...(href ? { href } : {}),
|
|
261
|
+
actions: { click: whole ? "copy" : "pick", ...(href ? { menu: ["copy", "open"] } : {}) },
|
|
262
|
+
},
|
|
263
|
+
[body],
|
|
264
|
+
key,
|
|
265
|
+
);
|
|
266
|
+
}
|
|
42
267
|
|
|
43
268
|
export interface CopySelectorDeps {
|
|
44
269
|
ui: TUI;
|
|
@@ -120,6 +345,15 @@ export class CopySelectorComponent implements Component {
|
|
|
120
345
|
/** True while older history is still unreplayed. */
|
|
121
346
|
#truncated = false;
|
|
122
347
|
|
|
348
|
+
/** Last described root and the state it was built from. */
|
|
349
|
+
#native: { memo: string; targets: OutlineTarget[]; blocks: CopyBlock[] | undefined; node: NativeNode } | undefined;
|
|
350
|
+
/** Transcript list items, rebuilt only when the replayed targets change. */
|
|
351
|
+
#nativeItems: { targets: OutlineTarget[]; truncated: boolean; items: NativeNode[] } | undefined;
|
|
352
|
+
/** Timeline picker items of the replayed targets. */
|
|
353
|
+
#pickerItems = new TimelineItems();
|
|
354
|
+
/** Last described picker and the state it was built from. */
|
|
355
|
+
#picker: { memo: string; targets: OutlineTarget[]; blocks: CopyBlock[] | undefined; node: NativeNode } | undefined;
|
|
356
|
+
|
|
123
357
|
constructor(
|
|
124
358
|
entries: TranscriptEntry[],
|
|
125
359
|
private readonly deps: CopySelectorDeps,
|
|
@@ -213,8 +447,7 @@ export class CopySelectorComponent implements Component {
|
|
|
213
447
|
return;
|
|
214
448
|
}
|
|
215
449
|
if (matchesSelectCancel(data) || matchesKey(data, "escape")) {
|
|
216
|
-
|
|
217
|
-
else this.deps.onCancel();
|
|
450
|
+
this.#escape();
|
|
218
451
|
return;
|
|
219
452
|
}
|
|
220
453
|
if (matchesAppToolsExpand(data)) {
|
|
@@ -232,14 +465,7 @@ export class CopySelectorComponent implements Component {
|
|
|
232
465
|
return;
|
|
233
466
|
}
|
|
234
467
|
if (matchesKey(data, "right")) {
|
|
235
|
-
|
|
236
|
-
const target = this.#targets[this.#selected];
|
|
237
|
-
if (!target) return;
|
|
238
|
-
const blocks = this.#blocksFor(target);
|
|
239
|
-
if (blocks.length === 0) return;
|
|
240
|
-
this.#blocks = blocks;
|
|
241
|
-
this.#blockSelected = 0;
|
|
242
|
-
this.deps.requestRender();
|
|
468
|
+
this.#descend();
|
|
243
469
|
return;
|
|
244
470
|
}
|
|
245
471
|
if (matchesKey(data, "left")) {
|
|
@@ -251,20 +477,11 @@ export class CopySelectorComponent implements Component {
|
|
|
251
477
|
return;
|
|
252
478
|
}
|
|
253
479
|
if (data === "o" || data === "O") {
|
|
254
|
-
|
|
255
|
-
if (block?.href && this.deps.onOpen) this.deps.onOpen(block.href, block.label);
|
|
480
|
+
this.#openSelectedBlock();
|
|
256
481
|
return;
|
|
257
482
|
}
|
|
258
483
|
if (matchesKey(data, "enter") || matchesKey(data, "return") || data === "\n") {
|
|
259
|
-
|
|
260
|
-
const block = this.#blocks[this.#blockSelected];
|
|
261
|
-
if (block) this.deps.onPick(block.content, block.label, { entry: block.entry, block });
|
|
262
|
-
return;
|
|
263
|
-
}
|
|
264
|
-
const target = this.#targets[this.#selected];
|
|
265
|
-
if (!target) return;
|
|
266
|
-
const item = targetCopy(target, this.#blocksFor(target));
|
|
267
|
-
this.deps.onPick(item.content, item.label, { entry: target.entries[0]! });
|
|
484
|
+
this.#pickOutlined();
|
|
268
485
|
return;
|
|
269
486
|
}
|
|
270
487
|
// Page/home/end/shift+arrow scrolling without moving the selection.
|
|
@@ -273,6 +490,42 @@ export class CopySelectorComponent implements Component {
|
|
|
273
490
|
}
|
|
274
491
|
}
|
|
275
492
|
|
|
493
|
+
/** Esc: leave the block view, else close. */
|
|
494
|
+
#escape(): void {
|
|
495
|
+
if (this.#blocks) this.#ascend();
|
|
496
|
+
else this.deps.onCancel();
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
/** Right: replace the outlined turn with its inner blocks. */
|
|
500
|
+
#descend(): void {
|
|
501
|
+
if (this.#blocks) return;
|
|
502
|
+
const target = this.#targets[this.#selected];
|
|
503
|
+
if (!target) return;
|
|
504
|
+
const blocks = this.#blocksFor(target);
|
|
505
|
+
if (blocks.length === 0) return;
|
|
506
|
+
this.#blocks = blocks;
|
|
507
|
+
this.#blockSelected = 0;
|
|
508
|
+
this.deps.requestRender();
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
/** Enter: pick the outlined block, or the outlined turn's text. */
|
|
512
|
+
#pickOutlined(): void {
|
|
513
|
+
if (this.#blocks) {
|
|
514
|
+
const block = this.#blocks[this.#blockSelected];
|
|
515
|
+
if (block) this.deps.onPick(block.content, block.label, { entry: block.entry, block });
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
518
|
+
const target = this.#targets[this.#selected];
|
|
519
|
+
if (!target) return;
|
|
520
|
+
const item = targetCopy(target, this.#blocksFor(target));
|
|
521
|
+
this.deps.onPick(item.content, item.label, { entry: target.entries[0]! });
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
#openSelectedBlock(): void {
|
|
525
|
+
const block = this.#blocks?.[this.#blockSelected];
|
|
526
|
+
if (block?.href && this.deps.onOpen) this.deps.onOpen(block.href, block.label);
|
|
527
|
+
}
|
|
528
|
+
|
|
276
529
|
#ascend(): void {
|
|
277
530
|
this.#blocks = undefined;
|
|
278
531
|
this.#blockSelected = 0;
|
|
@@ -324,6 +577,275 @@ export class CopySelectorComponent implements Component {
|
|
|
324
577
|
}
|
|
325
578
|
}
|
|
326
579
|
|
|
580
|
+
// ========================================================================
|
|
581
|
+
// Native
|
|
582
|
+
// ========================================================================
|
|
583
|
+
|
|
584
|
+
/** A `picker` is its own sheet: the backend mounts it in `layer` without a wrapper. */
|
|
585
|
+
nativeSheet(cx: DescribeContext): boolean {
|
|
586
|
+
return cx.supports("picker");
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
handleNativeEvent(event: NativeUiEvent): void {
|
|
590
|
+
const picked = pickerEvent(event);
|
|
591
|
+
if (picked) {
|
|
592
|
+
this.#handlePickerEvent(picked);
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
595
|
+
if (event.type === "action" && event.act === "pick") {
|
|
596
|
+
// A preview section too long to copy in the terminal: omp copies the full text.
|
|
597
|
+
this.#pickSection(event.key);
|
|
598
|
+
return;
|
|
599
|
+
}
|
|
600
|
+
if (event.type === "action") {
|
|
601
|
+
if (event.act === "blocks") this.#descend();
|
|
602
|
+
else if (event.act === "back") this.#ascend();
|
|
603
|
+
else if (event.act === "open") this.#openSelectedBlock();
|
|
604
|
+
else if (event.act === "copy") this.#pickOutlined();
|
|
605
|
+
return;
|
|
606
|
+
}
|
|
607
|
+
if (event.type !== "select" && event.type !== "activate") return;
|
|
608
|
+
// A click on an item outlines it and picks it, as Enter would.
|
|
609
|
+
if (event.key === "list" && !this.#blocks) {
|
|
610
|
+
if (event.item === EARLIER_TURNS_KEY) {
|
|
611
|
+
this.#loadFullHistory();
|
|
612
|
+
return;
|
|
613
|
+
}
|
|
614
|
+
const index = this.#targets.findIndex(target => target.turnId === event.item);
|
|
615
|
+
if (index < 0) return;
|
|
616
|
+
this.#selected = index;
|
|
617
|
+
} else if (event.key === "blocks" && this.#blocks) {
|
|
618
|
+
const index = Number(event.item);
|
|
619
|
+
if (!Number.isInteger(index) || !this.#blocks[index]) return;
|
|
620
|
+
this.#blockSelected = index;
|
|
621
|
+
} else {
|
|
622
|
+
return;
|
|
623
|
+
}
|
|
624
|
+
this.deps.requestRender();
|
|
625
|
+
this.#pickOutlined();
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
/** Row click selects a turn (leaving the block view), a second click copies it; the action bar runs its key. */
|
|
629
|
+
#handlePickerEvent(event: PickerEvent): void {
|
|
630
|
+
if (event.kind === "action") {
|
|
631
|
+
switch (event.act) {
|
|
632
|
+
case "pick":
|
|
633
|
+
this.#pickOutlined();
|
|
634
|
+
return;
|
|
635
|
+
case "blocks":
|
|
636
|
+
this.#descend();
|
|
637
|
+
return;
|
|
638
|
+
case "open-link":
|
|
639
|
+
this.#openSelectedBlock();
|
|
640
|
+
return;
|
|
641
|
+
case "earlier":
|
|
642
|
+
this.#loadFullHistory();
|
|
643
|
+
return;
|
|
644
|
+
case "close":
|
|
645
|
+
this.#escape();
|
|
646
|
+
return;
|
|
647
|
+
default:
|
|
648
|
+
return;
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
const index = this.#targets.findIndex(target => target.turnId === event.item);
|
|
652
|
+
if (index < 0) return;
|
|
653
|
+
if (index !== this.#selected || this.#blocks) {
|
|
654
|
+
this.#selected = index;
|
|
655
|
+
this.#blocks = undefined;
|
|
656
|
+
this.#blockSelected = 0;
|
|
657
|
+
this.deps.requestRender();
|
|
658
|
+
}
|
|
659
|
+
if (event.kind === "activate") this.#pickOutlined();
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
/** Pick the preview section keyed `key` (`whole` or `b<index>`) of the selected turn. */
|
|
663
|
+
#pickSection(key: string): void {
|
|
664
|
+
const target = this.#targets[this.#selected];
|
|
665
|
+
if (!target) return;
|
|
666
|
+
const blocks = this.#blocksFor(target);
|
|
667
|
+
if (key === "whole") {
|
|
668
|
+
const item = targetCopy(target, blocks);
|
|
669
|
+
this.deps.onPick(item.content, item.label, { entry: target.entries[0]! });
|
|
670
|
+
return;
|
|
671
|
+
}
|
|
672
|
+
const block = key.startsWith("b") ? blocks[Number(key.slice(1))] : undefined;
|
|
673
|
+
if (block) this.deps.onPick(block.content, block.label, { entry: block.entry, block });
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
describe(cx: DescribeContext): NativeNode {
|
|
677
|
+
return cx.supports("picker") ? this.#describePicker() : this.#describeCard();
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
/**
|
|
681
|
+
* The `timeline` picker: the turns as rows, the selected turn's blocks as
|
|
682
|
+
* preview sections (the whole message first), each copied by a click. In
|
|
683
|
+
* the block view the focused block's section takes the
|
|
684
|
+
* `omp.picker.block.focused` role and the preview owns the focus.
|
|
685
|
+
*/
|
|
686
|
+
#describePicker(): NativeNode {
|
|
687
|
+
const memo = `${this.#selected}|${this.#blockSelected}|${this.#truncated}`;
|
|
688
|
+
const cached = this.#picker;
|
|
689
|
+
if (cached?.memo === memo && cached.targets === this.#targets && cached.blocks === this.#blocks) {
|
|
690
|
+
return cached.node;
|
|
691
|
+
}
|
|
692
|
+
const target = this.#targets[this.#selected];
|
|
693
|
+
const blocks = target ? this.#blocksFor(target) : [];
|
|
694
|
+
const descended = this.#blocks !== undefined;
|
|
695
|
+
const focused = this.#blocks?.[this.#blockSelected];
|
|
696
|
+
const action = this.deps.actionLabel ?? "copy";
|
|
697
|
+
const props: TspPickerProps = {
|
|
698
|
+
title: this.deps.title ?? "Copy",
|
|
699
|
+
...(this.deps.title ? {} : { subtitle: "Pick what to put on the clipboard" }),
|
|
700
|
+
icon: "clipboard",
|
|
701
|
+
noun: "turns",
|
|
702
|
+
size: "lg",
|
|
703
|
+
layout: "timeline",
|
|
704
|
+
preview: "side",
|
|
705
|
+
query: null,
|
|
706
|
+
columns: TIMELINE_COLUMNS,
|
|
707
|
+
items: this.#pickerItems.of(this.#targets),
|
|
708
|
+
selected: target?.turnId ?? null,
|
|
709
|
+
total: this.#targets.length,
|
|
710
|
+
empty: "Nothing to copy",
|
|
711
|
+
focus: descended ? "preview" : "list",
|
|
712
|
+
actions: compact([
|
|
713
|
+
pickerAction("pick", `${action[0]!.toUpperCase()}${action.slice(1)}`, "enter", { primary: true }),
|
|
714
|
+
!descended && blocks.length > 0 ? pickerAction("blocks", "Blocks", "right") : undefined,
|
|
715
|
+
focused?.href && this.deps.onOpen ? pickerAction("open-link", "Open link", "o") : undefined,
|
|
716
|
+
!descended && this.#truncated ? pickerAction("earlier", "Earlier turns", "a") : undefined,
|
|
717
|
+
descended ? { ...CLOSE_ACTION, label: "Back" } : CLOSE_ACTION,
|
|
718
|
+
]),
|
|
719
|
+
};
|
|
720
|
+
const preview: NativeNode[] = [];
|
|
721
|
+
if (target) {
|
|
722
|
+
const whole = targetCopy(target, blocks);
|
|
723
|
+
if (whole.content.trim()) {
|
|
724
|
+
preview.push(
|
|
725
|
+
previewSection("whole", "Whole message", turnPreview(target, whole.content), whole.content, false),
|
|
726
|
+
);
|
|
727
|
+
}
|
|
728
|
+
blocks.forEach((block, index) => {
|
|
729
|
+
preview.push(
|
|
730
|
+
previewSection(
|
|
731
|
+
`b${index}`,
|
|
732
|
+
blockCaption(block),
|
|
733
|
+
blockBody(block),
|
|
734
|
+
block.content,
|
|
735
|
+
descended && index === this.#blockSelected,
|
|
736
|
+
block.href,
|
|
737
|
+
),
|
|
738
|
+
);
|
|
739
|
+
});
|
|
740
|
+
}
|
|
741
|
+
const root = picker(props, preview);
|
|
742
|
+
this.#picker = { memo, targets: this.#targets, blocks: this.#blocks, node: root };
|
|
743
|
+
return root;
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
#describeCard(): NativeNode {
|
|
747
|
+
const memo = `${this.#selected}|${this.#blockSelected}|${this.#truncated}`;
|
|
748
|
+
const cached = this.#native;
|
|
749
|
+
if (cached?.memo === memo && cached.targets === this.#targets && cached.blocks === this.#blocks) {
|
|
750
|
+
return cached.node;
|
|
751
|
+
}
|
|
752
|
+
const target = this.#targets[this.#selected];
|
|
753
|
+
const action = this.deps.actionLabel ?? "copy";
|
|
754
|
+
const upDown = actionHint(["tui.select.up", "tui.select.down"], this.#blocks ? "block" : "step");
|
|
755
|
+
const cancel = actionHint("tui.select.cancel", this.#blocks ? "back" : "close");
|
|
756
|
+
const children: NativeChild[] = [];
|
|
757
|
+
let hints: (NativeHint | undefined)[];
|
|
758
|
+
if (this.#blocks && target) {
|
|
759
|
+
const block = this.#blocks[this.#blockSelected];
|
|
760
|
+
children.push(
|
|
761
|
+
row([text([span("‹ back", "accent")], { actions: { click: "back" } })], { gap: "md" }),
|
|
762
|
+
node(
|
|
763
|
+
"list",
|
|
764
|
+
{ selected: String(this.#blockSelected), empty: "No blocks" },
|
|
765
|
+
this.#blocks.map((item, index) => {
|
|
766
|
+
const lines = item.content.split("\n").length;
|
|
767
|
+
return node(
|
|
768
|
+
"item",
|
|
769
|
+
{ label: item.label, detail: `${lines} line${lines === 1 ? "" : "s"}` },
|
|
770
|
+
undefined,
|
|
771
|
+
String(index),
|
|
772
|
+
);
|
|
773
|
+
}),
|
|
774
|
+
"blocks",
|
|
775
|
+
),
|
|
776
|
+
);
|
|
777
|
+
if (block) children.push(this.#blockPreview(block, action));
|
|
778
|
+
hints = [
|
|
779
|
+
upDown,
|
|
780
|
+
{ keys: ["left", ...(cancel?.keys ?? [])], label: "back" },
|
|
781
|
+
{ keys: ["enter"], label: action },
|
|
782
|
+
block?.href && this.deps.onOpen ? { keys: ["o"], label: "open" } : undefined,
|
|
783
|
+
];
|
|
784
|
+
} else {
|
|
785
|
+
const blocks = target ? this.#blocksFor(target) : [];
|
|
786
|
+
children.push(
|
|
787
|
+
node(
|
|
788
|
+
"list",
|
|
789
|
+
{ selected: target?.turnId ?? null, virtual: true, max: 0.5, empty: "Nothing to copy" },
|
|
790
|
+
this.#transcriptItems(),
|
|
791
|
+
"list",
|
|
792
|
+
),
|
|
793
|
+
);
|
|
794
|
+
if (target) {
|
|
795
|
+
const item = targetCopy(target, blocks);
|
|
796
|
+
const body: NativeChild[] = [turnPreview(target, item.content)];
|
|
797
|
+
if (blocks.length > 0) {
|
|
798
|
+
body.push(
|
|
799
|
+
text([span(`${blocks.length} block${blocks.length === 1 ? "" : "s"} ›`, "accent")], {
|
|
800
|
+
actions: { click: "blocks" },
|
|
801
|
+
}),
|
|
802
|
+
);
|
|
803
|
+
}
|
|
804
|
+
children.push(node("section", { head: [span(item.label, "strong")] }, body, "preview"));
|
|
805
|
+
}
|
|
806
|
+
hints = [
|
|
807
|
+
upDown,
|
|
808
|
+
blocks.length > 0 ? { keys: ["right"], label: "blocks" } : undefined,
|
|
809
|
+
{ keys: ["enter"], label: action },
|
|
810
|
+
this.#truncated ? { keys: ["a"], label: "earlier turns" } : undefined,
|
|
811
|
+
cancel,
|
|
812
|
+
];
|
|
813
|
+
}
|
|
814
|
+
children.push(hintsRow(hints));
|
|
815
|
+
const head: TspText = this.deps.title ?? [
|
|
816
|
+
span(`${theme.cmd.copy} `),
|
|
817
|
+
span("Copy", "strong"),
|
|
818
|
+
span(`${theme.sep.dot}pick what to put on the clipboard`, "dim"),
|
|
819
|
+
];
|
|
820
|
+
const root = overlayCard("omp.overlay.copy", head, children);
|
|
821
|
+
this.#native = { memo, targets: this.#targets, blocks: this.#blocks, node: root };
|
|
822
|
+
return root;
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
#transcriptItems(): NativeNode[] {
|
|
826
|
+
const cached = this.#nativeItems;
|
|
827
|
+
if (cached?.targets === this.#targets && cached.truncated === this.#truncated) return cached.items;
|
|
828
|
+
const items = this.#targets.map(target => turnItem(target));
|
|
829
|
+
if (this.#truncated) items.unshift(earlierTurnsItem);
|
|
830
|
+
this.#nativeItems = { targets: this.#targets, truncated: this.#truncated, items };
|
|
831
|
+
return items;
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
/** The selected block in full: highlighted code, a link, a quote, or plain output. */
|
|
835
|
+
#blockPreview(block: CopyBlock, action: string): NativeNode {
|
|
836
|
+
let body: NativeNode;
|
|
837
|
+
if (block.href) body = text([span(block.href, "link")], { wrap: "char" });
|
|
838
|
+
else if (block.kind === "quote") body = md(block.content);
|
|
839
|
+
else body = code(block.content, { lang: block.language, wrap: true });
|
|
840
|
+
const controls: NativeChild[] = [
|
|
841
|
+
text([span(`${theme.cmd.copy} ${action}`, "accent")], { actions: { click: "copy" } }),
|
|
842
|
+
];
|
|
843
|
+
if (block.href && this.deps.onOpen) {
|
|
844
|
+
controls.push(text([span(`${theme.cmd.share} open`, "accent")], { actions: { click: "open" } }));
|
|
845
|
+
}
|
|
846
|
+
return node("section", { head: [span(block.label, "strong")] }, [body, row(controls, { gap: "md" })], "preview");
|
|
847
|
+
}
|
|
848
|
+
|
|
327
849
|
// ========================================================================
|
|
328
850
|
// Render
|
|
329
851
|
// ========================================================================
|
|
@@ -530,7 +1052,7 @@ function pushMarkdownBlocks(blocks: CopyBlock[], text: string, entry: Transcript
|
|
|
530
1052
|
}
|
|
531
1053
|
|
|
532
1054
|
/** Inner blocks of one turn: markdown code/quotes, commands, and tool output. */
|
|
533
|
-
function collectBlocks(entries: readonly TranscriptEntry[]): CopyBlock[] {
|
|
1055
|
+
export function collectBlocks(entries: readonly TranscriptEntry[]): CopyBlock[] {
|
|
534
1056
|
const blocks: CopyBlock[] = [];
|
|
535
1057
|
for (const entry of entries) {
|
|
536
1058
|
const message = transcriptEntryMessage(entry);
|
|
@@ -577,7 +1099,7 @@ function collectBlocks(entries: readonly TranscriptEntry[]): CopyBlock[] {
|
|
|
577
1099
|
}
|
|
578
1100
|
|
|
579
1101
|
/** Clipboard payload for a whole turn, falling back to its blocks when the turn has no prose. */
|
|
580
|
-
function targetCopy(target: OutlineTarget, blocks: readonly CopyBlock[]): { content: string; label: string } {
|
|
1102
|
+
export function targetCopy(target: OutlineTarget, blocks: readonly CopyBlock[]): { content: string; label: string } {
|
|
581
1103
|
const entry = target.entries[0]!;
|
|
582
1104
|
const message = transcriptEntryMessage(entry);
|
|
583
1105
|
switch (message?.role) {
|