@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
|
@@ -5,11 +5,17 @@ import { ImageProtocol, TERMINAL } from "../terminal-capabilities";
|
|
|
5
5
|
import { Markdown, type MarkdownTheme } from "../components/markdown";
|
|
6
6
|
import { Spacer } from "../components/spacer";
|
|
7
7
|
import { Text } from "../components/text";
|
|
8
|
-
import { formatNumber } from "@oh-my-pi/pi-utils";
|
|
8
|
+
import { formatDuration, formatNumber } from "@oh-my-pi/pi-utils";
|
|
9
9
|
import chalk from "@oh-my-pi/pi-utils/chalk";
|
|
10
10
|
import { LRUCache } from "@oh-my-pi/pi-utils/lru";
|
|
11
11
|
import type { AssistantThinkingRenderer } from "./extension-types";
|
|
12
|
-
import { ensureThemeSync, getMarkdownTheme, theme } from "../theme";
|
|
12
|
+
import { ensureThemeSync, getMarkdownTheme, getThemeEpoch, theme } from "../theme";
|
|
13
|
+
import { card, col, elapsed, node, span, text } from "../native/describe";
|
|
14
|
+
import { hasTranscriptActions, runTranscriptAction } from "./transcript-actions";
|
|
15
|
+
import type { NativeChild, NativeNode, NativeUiEvent } from "../native/node";
|
|
16
|
+
import { isNativeRendering } from "../native/state";
|
|
17
|
+
import { NativeImageCache } from "../native/blobs";
|
|
18
|
+
import { Memo } from "../native/memo";
|
|
13
19
|
import { EMPTY_LINK_TARGETS, resolveImageOptions } from "../render/render-utils";
|
|
14
20
|
import { WidthAwareText } from "../render";
|
|
15
21
|
import { cachedPngConversion, convertImageToPngShared, imagePayloadKey } from "./image-loading";
|
|
@@ -20,6 +26,7 @@ import { formatErrorBlock } from "../chrome/error-block";
|
|
|
20
26
|
import { type ServedModelMismatch, ServedModelMarkerComponent } from "./served-model-marker";
|
|
21
27
|
import { isReactionTarget, type ReactionSplit, type ReactionTarget, splitReaction } from "./reaction";
|
|
22
28
|
import { isRowPrefix, type TranscriptStableRow, trimBlankEdges } from "../chrome/transcript-container";
|
|
29
|
+
import { formatTurnUsage, type TurnUsageSummary } from "../overlays/usage-row";
|
|
23
30
|
|
|
24
31
|
/**
|
|
25
32
|
* Max wrapped rows of a turn-ending provider error rendered inline in the
|
|
@@ -31,9 +38,17 @@ import { isRowPrefix, type TranscriptStableRow, trimBlankEdges } from "../chrome
|
|
|
31
38
|
const MAX_TRANSCRIPT_ERROR_ROWS = 8;
|
|
32
39
|
const EMPTY_STABLE_RENDER: readonly string[] = [];
|
|
33
40
|
|
|
41
|
+
/** The native head of a finished thinking block: "Thought for 12s", or "Thought" when it was never seen streaming. */
|
|
42
|
+
function thoughtLabel(clock: { start: number; end?: number } | undefined): string {
|
|
43
|
+
if (clock?.end === undefined) return "Thought";
|
|
44
|
+
const ms = clock.end - clock.start;
|
|
45
|
+
return `Thought for ${ms < 60_000 ? `${Math.max(1, Math.round(ms / 1000))}s` : formatDuration(ms)}`;
|
|
46
|
+
}
|
|
47
|
+
|
|
34
48
|
type ThinkingContentBlock = Extract<AssistantMessage["content"][number], { type: "thinking" }>;
|
|
35
49
|
type DisplayThinkingContentBlock = ThinkingContentBlock & { rawThinking?: string };
|
|
36
|
-
type
|
|
50
|
+
type StablePartKind = "thinking" | "text";
|
|
51
|
+
type StablePart = { kind: StablePartKind; text: string } | { kind: "spacer" };
|
|
37
52
|
|
|
38
53
|
/**
|
|
39
54
|
* One published prefix of the block's finished content. Later snapshots extend
|
|
@@ -47,6 +62,37 @@ interface StableSnapshot {
|
|
|
47
62
|
readonly lastTextLength: number;
|
|
48
63
|
}
|
|
49
64
|
|
|
65
|
+
/**
|
|
66
|
+
* Stable-row renders at one width. `rows` renders snapshot `newest`; every
|
|
67
|
+
* count in `ends` renders byte-identically to `rows.slice(0, end)` — checked
|
|
68
|
+
* against `rows` when recorded — so an earlier prefix is a slice of the newest
|
|
69
|
+
* render instead of a re-render, and one row array stays resident per width.
|
|
70
|
+
*/
|
|
71
|
+
interface StableRowLedger {
|
|
72
|
+
newest: number;
|
|
73
|
+
rows: readonly string[];
|
|
74
|
+
readonly ends: Map<number, number>;
|
|
75
|
+
/** Rendered rows of parts a snapshot has closed (full text final), by part index. */
|
|
76
|
+
readonly parts: (
|
|
77
|
+
| { readonly kind: StablePartKind; readonly text: string; readonly rows: readonly string[] }
|
|
78
|
+
| undefined
|
|
79
|
+
)[];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** One Markdown instance reused while a stable part's text grows between renders. */
|
|
83
|
+
interface StablePartRenderer {
|
|
84
|
+
readonly index: number;
|
|
85
|
+
readonly kind: StablePartKind;
|
|
86
|
+
readonly md: Markdown;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** Theme inputs cached stable renders were produced with; any change drops them. */
|
|
90
|
+
interface StableRenderInputs {
|
|
91
|
+
readonly prose: MarkdownTheme;
|
|
92
|
+
readonly markdown: MarkdownTheme;
|
|
93
|
+
readonly color: ((text: string) => string) | undefined;
|
|
94
|
+
}
|
|
95
|
+
|
|
50
96
|
function isSnapshotExtension(previous: readonly StablePart[], current: readonly StablePart[]): boolean {
|
|
51
97
|
if (previous.length > current.length) return false;
|
|
52
98
|
for (let index = 0; index < previous.length; index++) {
|
|
@@ -219,6 +265,12 @@ export class AssistantMessageComponent extends Container {
|
|
|
219
265
|
* provider error whose tail would otherwise be unreachable in the live TUI.
|
|
220
266
|
*/
|
|
221
267
|
#errorExpanded = false;
|
|
268
|
+
/** The deduplicated message the native error frame shows (its "Copy error" copies it). */
|
|
269
|
+
#errorText: string | undefined;
|
|
270
|
+
/** A TTSR rule aborted this text; the turn re-streams below it. */
|
|
271
|
+
#rewound = false;
|
|
272
|
+
/** The turn's totals when this answer ends it; a TSP terminal shows them under the answer. */
|
|
273
|
+
#turnUsage: TurnUsageSummary | undefined;
|
|
222
274
|
/**
|
|
223
275
|
* True when the current {@link updateContent} message carries a truncatable
|
|
224
276
|
* inline provider error (the `#appendErrorBlock` path) — set whether or not
|
|
@@ -260,13 +312,20 @@ export class AssistantMessageComponent extends Container {
|
|
|
260
312
|
#nextStableRowId = 0;
|
|
261
313
|
#transcriptStableRows: TranscriptStableRow[] = [];
|
|
262
314
|
/**
|
|
263
|
-
*
|
|
264
|
-
* for several
|
|
265
|
-
* projected
|
|
266
|
-
*
|
|
267
|
-
*
|
|
315
|
+
* Verified stable-row renders per width ({@link StableRowLedger}). The
|
|
316
|
+
* container asks for several counts per frame (emitted, offered end,
|
|
317
|
+
* projected) and each publication re-checks the previous prefix; the ledger
|
|
318
|
+
* answers all of them from the newest render. A few widths cover resizes.
|
|
319
|
+
* Cleared on reset, finalize, and theme change.
|
|
268
320
|
*/
|
|
269
|
-
#
|
|
321
|
+
#stableLedgers = new LRUCache<number, StableRowLedger>({ max: 4 });
|
|
322
|
+
/** Prefixes handed out as ledger slices or rendered off-ledger, by `${count}:${width}`. */
|
|
323
|
+
#stableRenderCache = new LRUCache<string, readonly string[]>({ max: 8 });
|
|
324
|
+
/** Reused for the growing final part of each publication candidate. */
|
|
325
|
+
#stableHeadRenderer: StablePartRenderer | undefined;
|
|
326
|
+
/** Reused for final parts of published snapshots rendered off-ledger (reflow, replay). */
|
|
327
|
+
#stableReplayRenderer: StablePartRenderer | undefined;
|
|
328
|
+
#stableRenderInputs: StableRenderInputs | undefined;
|
|
270
329
|
/** Provider-reported tokens in the live thinking block — reasoning tokens when
|
|
271
330
|
* the provider streams them, else total output — shown dimmed beside the
|
|
272
331
|
* speed badge. 0 when no thinking is streaming. */
|
|
@@ -284,6 +343,19 @@ export class AssistantMessageComponent extends Container {
|
|
|
284
343
|
#reactionTarget: ReactionTarget | undefined;
|
|
285
344
|
/** Reaction lifted from the reply's opening emoji, once resolved. */
|
|
286
345
|
#reaction: string | undefined;
|
|
346
|
+
/** Display form of {@link #lastMessage} (reaction handled) the native description is built from. */
|
|
347
|
+
#displayedMessage: AssistantMessage | undefined;
|
|
348
|
+
/** Thinking-extension components per content index, recorded when the slow path mounts them. */
|
|
349
|
+
#thinkingExtensions = new Map<number, Component[]>();
|
|
350
|
+
/** Collapse state of thinking sections toggled in the terminal, by content index; cleared by {@link setHideThinkingBlock}. */
|
|
351
|
+
#thinkingCollapsed = new Map<number, boolean>();
|
|
352
|
+
/** When each thinking block was seen streaming and when it stopped (native "Thought for 12s"), by content index. */
|
|
353
|
+
#thinkingClock = new Map<number, { start: number; end?: number; tokens: number }>();
|
|
354
|
+
#nativeViewVersion = 0;
|
|
355
|
+
readonly #native = new Memo();
|
|
356
|
+
/** Markdown nodes by key, reused while their text and streaming flag are unchanged. */
|
|
357
|
+
#nativeParts = new Map<string, { text: string; stream: boolean; node: NativeNode }>();
|
|
358
|
+
readonly #nativeImages = new NativeImageCache();
|
|
287
359
|
|
|
288
360
|
setTextColorTransform(transform?: (text: string) => string): void {
|
|
289
361
|
this.#textColorTransform = transform;
|
|
@@ -430,6 +502,12 @@ export class AssistantMessageComponent extends Container {
|
|
|
430
502
|
this.#refreshMarkers();
|
|
431
503
|
}
|
|
432
504
|
|
|
505
|
+
/** Mark this answer as the end of a turn with the turn's totals (native only: ANSI keeps them in the status line). */
|
|
506
|
+
setTurnUsage(summary: TurnUsageSummary | undefined): void {
|
|
507
|
+
this.#turnUsage = summary;
|
|
508
|
+
this.#nativeViewVersion++;
|
|
509
|
+
}
|
|
510
|
+
|
|
433
511
|
/**
|
|
434
512
|
* Show or clear the trailing served-model divider. Set once the turn's
|
|
435
513
|
* signed thinking block (or router report) has named the model that actually
|
|
@@ -463,6 +541,7 @@ export class AssistantMessageComponent extends Container {
|
|
|
463
541
|
|
|
464
542
|
setHideThinkingBlock(hide: boolean): void {
|
|
465
543
|
this.#hideThinkingBlock = hide;
|
|
544
|
+
this.#thinkingCollapsed.clear();
|
|
466
545
|
}
|
|
467
546
|
|
|
468
547
|
/**
|
|
@@ -495,14 +574,30 @@ export class AssistantMessageComponent extends Container {
|
|
|
495
574
|
* Once text starts, a tool call streams, or the block is sealed, the pulse ends.
|
|
496
575
|
*/
|
|
497
576
|
#shouldAnimateThinking(message: AssistantMessage): boolean {
|
|
498
|
-
|
|
577
|
+
return this.#hideThinkingBlock && this.#thinkingTailIndex(message) !== undefined;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
/**
|
|
581
|
+
* Content index of the thinking block the model is producing right now:
|
|
582
|
+
* the block is still streaming (not finalized), no tool call has started,
|
|
583
|
+
* and the tail visible block is thinking. Undefined otherwise.
|
|
584
|
+
*/
|
|
585
|
+
#thinkingTailIndex(message: AssistantMessage): number | undefined {
|
|
586
|
+
if (this.#transcriptBlockFinalized) return undefined;
|
|
499
587
|
let tail: "text" | "thinking" | undefined;
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
588
|
+
let tailIndex = -1;
|
|
589
|
+
for (let index = 0; index < message.content.length; index++) {
|
|
590
|
+
const content = message.content[index]!;
|
|
591
|
+
if (content.type === "toolCall") return undefined;
|
|
592
|
+
if (content.type === "text" && canonicalizeMessage(content.text)) {
|
|
593
|
+
tail = "text";
|
|
594
|
+
tailIndex = index;
|
|
595
|
+
} else if (content.type === "thinking" && canonicalizeMessage(content.thinking)) {
|
|
596
|
+
tail = "thinking";
|
|
597
|
+
tailIndex = index;
|
|
598
|
+
}
|
|
504
599
|
}
|
|
505
|
-
return tail === "thinking";
|
|
600
|
+
return tail === "thinking" ? tailIndex : undefined;
|
|
506
601
|
}
|
|
507
602
|
|
|
508
603
|
#thinkingDotsLabel(): string {
|
|
@@ -532,7 +627,8 @@ export class AssistantMessageComponent extends Container {
|
|
|
532
627
|
}
|
|
533
628
|
|
|
534
629
|
#startThinkingAnimation(): void {
|
|
535
|
-
|
|
630
|
+
// A native terminal clocks the described starburst itself.
|
|
631
|
+
if (this.#thinkingDotsTimer || isNativeRendering()) return;
|
|
536
632
|
this.#scheduleThinkingFrame();
|
|
537
633
|
}
|
|
538
634
|
|
|
@@ -553,7 +649,7 @@ export class AssistantMessageComponent extends Container {
|
|
|
553
649
|
|
|
554
650
|
#advanceThinkingDots(): void {
|
|
555
651
|
this.#thinkingDotsTimer = undefined;
|
|
556
|
-
if (!this.#thinkingDots) {
|
|
652
|
+
if (!this.#thinkingDots || isNativeRendering()) {
|
|
557
653
|
this.#stopThinkingAnimation();
|
|
558
654
|
return;
|
|
559
655
|
}
|
|
@@ -610,6 +706,276 @@ export class AssistantMessageComponent extends Container {
|
|
|
610
706
|
return rows;
|
|
611
707
|
}
|
|
612
708
|
|
|
709
|
+
/**
|
|
710
|
+
* A `col` (role `omp.assistant`) of `md` nodes keyed by content index, so
|
|
711
|
+
* streamed deltas reach the terminal as `text append` on the same node;
|
|
712
|
+
* the tail block carries `stream: true` until the message finalizes.
|
|
713
|
+
* Thinking blocks are quiet collapsible `section`s (collapsed per
|
|
714
|
+
* `hideThinkingBlock`): a muted "Thinking…" shimmer while live, then
|
|
715
|
+
* "Thought for 12s"; tokens and rate ride in the head's `title`.
|
|
716
|
+
*/
|
|
717
|
+
override describe(): NativeNode {
|
|
718
|
+
const tail = this.#displayedMessage ? this.#thinkingTailIndex(this.#displayedMessage) : undefined;
|
|
719
|
+
const rate =
|
|
720
|
+
tail !== undefined && this.#lastUpdateTransient && this.#thinkingRateLive
|
|
721
|
+
? Math.round(Math.min(SPEED_MAX, sharedSpeedTracker.getSpeed()) * 10) / 10
|
|
722
|
+
: 0;
|
|
723
|
+
const key = [
|
|
724
|
+
this.#blockVersion,
|
|
725
|
+
this.#transcriptBlockFinalized,
|
|
726
|
+
this.#hideThinkingBlock,
|
|
727
|
+
this.#showImages,
|
|
728
|
+
this.#showToolResultImages,
|
|
729
|
+
this.#errorExpanded,
|
|
730
|
+
this.#nativeViewVersion,
|
|
731
|
+
rate,
|
|
732
|
+
getThemeEpoch(),
|
|
733
|
+
];
|
|
734
|
+
return this.#native.get(key, () => this.#describeMessage(tail, rate));
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
/**
|
|
738
|
+
* A TTSR rule rewound this partial answer: a native terminal dims it and
|
|
739
|
+
* tags it `↺ rewound` (the ANSI render leaves it as it stopped).
|
|
740
|
+
*/
|
|
741
|
+
markRewound(): void {
|
|
742
|
+
if (this.#rewound) return;
|
|
743
|
+
this.#rewound = true;
|
|
744
|
+
this.#nativeViewVersion++;
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
/** Mirrors a thinking section collapsed or expanded in the terminal. */
|
|
748
|
+
handleNativeEvent(event: NativeUiEvent): void {
|
|
749
|
+
if (event.type === "action") {
|
|
750
|
+
this.#handleErrorAction(event.act);
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
753
|
+
if (event.type !== "toggle" || !event.key.startsWith("k")) return;
|
|
754
|
+
const index = Number(event.key.slice(1));
|
|
755
|
+
if (!Number.isInteger(index)) return;
|
|
756
|
+
this.#thinkingCollapsed.set(index, event.collapsed);
|
|
757
|
+
this.#nativeViewVersion++;
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
#describeMessage(thinkingTail: number | undefined, rate: number): NativeNode {
|
|
761
|
+
const message = this.#displayedMessage;
|
|
762
|
+
const previousParts = this.#nativeParts;
|
|
763
|
+
const parts = new Map<string, { text: string; stream: boolean; node: NativeNode }>();
|
|
764
|
+
// `slot` identifies the node across rebuilds; `key` is its sibling key inside its parent.
|
|
765
|
+
const markdown = (slot: string, key: string, source: string, stream: boolean): NativeNode => {
|
|
766
|
+
const cached = previousParts.get(slot);
|
|
767
|
+
const entry =
|
|
768
|
+
cached?.text === source && cached.stream === stream
|
|
769
|
+
? cached
|
|
770
|
+
: {
|
|
771
|
+
text: source,
|
|
772
|
+
stream,
|
|
773
|
+
node: node("md", stream ? { text: source, stream: true } : { text: source }, undefined, key),
|
|
774
|
+
};
|
|
775
|
+
parts.set(slot, entry);
|
|
776
|
+
return entry.node;
|
|
777
|
+
};
|
|
778
|
+
const children: NativeChild[] = [];
|
|
779
|
+
if (message) {
|
|
780
|
+
const live = !this.#transcriptBlockFinalized && this.#lastUpdateTransient;
|
|
781
|
+
let tailIndex = -1;
|
|
782
|
+
for (let index = 0; index < message.content.length; index++) {
|
|
783
|
+
const content = message.content[index]!;
|
|
784
|
+
if (
|
|
785
|
+
(content.type === "text" && canonicalizeMessage(content.text)) ||
|
|
786
|
+
(content.type === "thinking" && canonicalizeMessage(content.thinking))
|
|
787
|
+
) {
|
|
788
|
+
tailIndex = index;
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
for (let index = 0; index < message.content.length; index++) {
|
|
792
|
+
const content = message.content[index]!;
|
|
793
|
+
const streaming = live && index === tailIndex;
|
|
794
|
+
if (content.type === "text" && canonicalizeMessage(content.text)) {
|
|
795
|
+
children.push(markdown(`t${index}`, `t${index}`, content.text.trim(), streaming));
|
|
796
|
+
} else if (content.type === "thinking") {
|
|
797
|
+
const display = resolveThinkingDisplay(content, this.#proseOnlyThinking);
|
|
798
|
+
if (!display.visible) continue;
|
|
799
|
+
const thinkingLive = streaming && thinkingTail === index;
|
|
800
|
+
const clock = this.#thinkingClock.get(index);
|
|
801
|
+
if (thinkingLive) {
|
|
802
|
+
if (clock) clock.tokens = this.#thinkingTokens || clock.tokens;
|
|
803
|
+
else this.#thinkingClock.set(index, { start: performance.now(), tokens: this.#thinkingTokens });
|
|
804
|
+
} else if (clock && clock.end === undefined) {
|
|
805
|
+
clock.end = performance.now();
|
|
806
|
+
}
|
|
807
|
+
const tokens = thinkingLive ? this.#thinkingTokens : (clock?.tokens ?? 0);
|
|
808
|
+
const title = tokens > 0 ? `${formatNumber(tokens)} tokens` : undefined;
|
|
809
|
+
// Live: starburst · "Thinking…" · ticking timer · tok/s. Done: "Thought for 12s".
|
|
810
|
+
const head = thinkingLive
|
|
811
|
+
? node(
|
|
812
|
+
"row",
|
|
813
|
+
{ gap: "sm", title },
|
|
814
|
+
[
|
|
815
|
+
node("spinner", { style: "starburst", role: "omp.thinking.spin" }),
|
|
816
|
+
text([span("Thinking…", "muted")]),
|
|
817
|
+
elapsed(performance.now() - (this.#thinkingClock.get(index)?.start ?? performance.now())),
|
|
818
|
+
...(rate >= 0.05 ? [node("rate", { value: rate, unit: "tok/s" })] : []),
|
|
819
|
+
],
|
|
820
|
+
"head",
|
|
821
|
+
)
|
|
822
|
+
: node("text", { spans: [span(thoughtLabel(clock), "muted")], title }, undefined, "head");
|
|
823
|
+
const body = markdown(`k${index}`, "body", display.text, streaming);
|
|
824
|
+
children.push(
|
|
825
|
+
node(
|
|
826
|
+
"section",
|
|
827
|
+
{
|
|
828
|
+
// `.live` while streaming: the body clamps to its tail under a fade.
|
|
829
|
+
// `.ghost` while thinking is hidden (Ctrl+T): only a faint "Thought for 12s" stays.
|
|
830
|
+
role: thinkingLive
|
|
831
|
+
? "omp.thinking.live"
|
|
832
|
+
: this.#hideThinkingBlock
|
|
833
|
+
? "omp.thinking.ghost"
|
|
834
|
+
: "omp.thinking",
|
|
835
|
+
collapsible: true,
|
|
836
|
+
// Open while it streams; a finished thought folds to its "Thought for 12s" line.
|
|
837
|
+
collapsed: this.#thinkingCollapsed.get(index) ?? (this.#hideThinkingBlock || !thinkingLive),
|
|
838
|
+
// Tern's fold head sums it into "Worked for 12s".
|
|
839
|
+
took: clock?.end === undefined ? undefined : Math.max(0, Math.round(clock.end - clock.start)),
|
|
840
|
+
},
|
|
841
|
+
[head, body],
|
|
842
|
+
`k${index}`,
|
|
843
|
+
),
|
|
844
|
+
);
|
|
845
|
+
if (!this.#hideThinkingBlock) children.push(...(this.#thinkingExtensions.get(index) ?? []));
|
|
846
|
+
} else if (content.type === "image" && content.data && content.mimeType && this.#showImages) {
|
|
847
|
+
children.push(this.#nativeImages.get(`i${index}`, content.data, content.mimeType));
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
if (this.#showImages && this.#showToolResultImages) {
|
|
851
|
+
for (const [toolCallId, images] of this.#toolImagesByCallId) {
|
|
852
|
+
images.forEach((image, index) => {
|
|
853
|
+
children.push(this.#nativeImages.get(`r${toolCallId}:${index}`, image.data, image.mimeType));
|
|
854
|
+
});
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
const errorNode = this.#describeError(message);
|
|
858
|
+
if (errorNode) children.push(errorNode);
|
|
859
|
+
}
|
|
860
|
+
this.#nativeParts = parts;
|
|
861
|
+
children.push(...this.#markerSlot.children);
|
|
862
|
+
if (this.#rewound) {
|
|
863
|
+
children.push(
|
|
864
|
+
node(
|
|
865
|
+
"badge",
|
|
866
|
+
{ text: "↺ rewound", tone: "muted", role: "omp.assistant.rewound-tag" },
|
|
867
|
+
undefined,
|
|
868
|
+
"rewound",
|
|
869
|
+
),
|
|
870
|
+
);
|
|
871
|
+
}
|
|
872
|
+
if (this.#turnUsage) {
|
|
873
|
+
const usage = formatTurnUsage(this.#turnUsage);
|
|
874
|
+
children.push(
|
|
875
|
+
node(
|
|
876
|
+
"row",
|
|
877
|
+
{ role: "omp.turn.usage", gap: "xs", align: "center", title: usage.title },
|
|
878
|
+
[
|
|
879
|
+
node("icon", { name: "time" }, undefined, "icon"),
|
|
880
|
+
node("text", { text: usage.text }, undefined, "text"),
|
|
881
|
+
],
|
|
882
|
+
"turn-usage",
|
|
883
|
+
),
|
|
884
|
+
);
|
|
885
|
+
}
|
|
886
|
+
return col(children, { role: this.#rewound ? "omp.assistant.rewound" : "omp.assistant" });
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
/**
|
|
890
|
+
* Turn-ending error, recovered-retry note or abort label. A failed request
|
|
891
|
+
* is one error frame (head: "Request failed" + the HTTP status chip; body:
|
|
892
|
+
* the message once; then Retry / Copy error / Switch model), and stays in
|
|
893
|
+
* the transcript: the pinned banner is ANSI-only. A recovered attempt is an
|
|
894
|
+
* inline row that discloses the original error.
|
|
895
|
+
*/
|
|
896
|
+
#describeError(message: AssistantMessage): NativeNode | undefined {
|
|
897
|
+
const presentation = resolveAssistantErrorPresentation(message);
|
|
898
|
+
if (presentation.kind === "compact-recovered") {
|
|
899
|
+
const attempt = message.retryRecovery?.attempt ?? 1;
|
|
900
|
+
const body = [message.errorMessage?.trim(), presentation.text].filter(
|
|
901
|
+
(line, index, all): line is string => !!line && all.indexOf(line) === index,
|
|
902
|
+
);
|
|
903
|
+
return node(
|
|
904
|
+
"section",
|
|
905
|
+
{
|
|
906
|
+
role: "omp.assistant.recovered",
|
|
907
|
+
head: [span(`↻ Recovered after ${attempt} ${attempt === 1 ? "retry" : "retries"}`, "muted")],
|
|
908
|
+
collapsible: true,
|
|
909
|
+
collapsed: true,
|
|
910
|
+
},
|
|
911
|
+
[text([span(body.join("\n"), "mono")], { wrap: "word" })],
|
|
912
|
+
"error",
|
|
913
|
+
);
|
|
914
|
+
}
|
|
915
|
+
if (presentation.kind !== "full" || message.content.some(content => content.type === "toolCall"))
|
|
916
|
+
return undefined;
|
|
917
|
+
if (message.stopReason === "aborted") {
|
|
918
|
+
return text([span(presentation.text, "error")], { wrap: "word", key: "error", role: "omp.assistant.abort" });
|
|
919
|
+
}
|
|
920
|
+
const lines = presentation.text
|
|
921
|
+
.split("\n")
|
|
922
|
+
.map(line => line.trim())
|
|
923
|
+
.filter(line => line.length > 0);
|
|
924
|
+
// "500 upstream overloaded" → chip "500", then the message once: a
|
|
925
|
+
// line the next one repeats with more detail is dropped.
|
|
926
|
+
const code = /^(\d{3})\s+/.exec(lines[0] ?? "");
|
|
927
|
+
if (code) lines[0] = lines[0]!.slice(code[0].length);
|
|
928
|
+
const detail = lines.filter((line, index) => !lines.slice(index + 1).some(next => next.startsWith(line)));
|
|
929
|
+
const errorText = detail.join("\n") || "Unknown error";
|
|
930
|
+
const children: NativeChild[] = [
|
|
931
|
+
node(
|
|
932
|
+
"row",
|
|
933
|
+
{ gap: "sm" },
|
|
934
|
+
[
|
|
935
|
+
text([span("Request failed", "error strong")]),
|
|
936
|
+
...(code ? [node("badge", { text: code[1]!, tone: "error", role: "omp.error.code" })] : []),
|
|
937
|
+
],
|
|
938
|
+
"head",
|
|
939
|
+
),
|
|
940
|
+
text([span(errorText, "mono")], {
|
|
941
|
+
wrap: "word",
|
|
942
|
+
lines: this.#errorExpanded ? undefined : MAX_TRANSCRIPT_ERROR_ROWS,
|
|
943
|
+
role: "omp.error.message",
|
|
944
|
+
key: "message",
|
|
945
|
+
}),
|
|
946
|
+
];
|
|
947
|
+
if (message.stopReason === "error" && hasTranscriptActions()) {
|
|
948
|
+
const button = (act: string, label: string, keys: readonly string[], title: string): NativeNode =>
|
|
949
|
+
node(
|
|
950
|
+
"row",
|
|
951
|
+
{ gap: "xs", role: "omp.error.action", actions: { click: act }, title },
|
|
952
|
+
keys.length > 0 ? [text(label), node("kbd", { keys })] : [text(label)],
|
|
953
|
+
act,
|
|
954
|
+
);
|
|
955
|
+
children.push(
|
|
956
|
+
node(
|
|
957
|
+
"row",
|
|
958
|
+
{ gap: "sm", role: "omp.error.actions" },
|
|
959
|
+
[
|
|
960
|
+
button("retry", "Retry", ["F5"], "Retry the failed turn"),
|
|
961
|
+
button("copy-error", "Copy error", [], "Copy the error message"),
|
|
962
|
+
button("switch-model", "Switch model", ["⌥P"], "Pick another model for this session"),
|
|
963
|
+
],
|
|
964
|
+
"actions",
|
|
965
|
+
),
|
|
966
|
+
);
|
|
967
|
+
}
|
|
968
|
+
this.#errorText = errorText;
|
|
969
|
+
return card({ role: "omp.error", tone: "error", key: "error" }, children);
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
/** Error frame action clicks: omp's own retry, clipboard and model-picker paths. */
|
|
973
|
+
#handleErrorAction(act: string): void {
|
|
974
|
+
if (act === "retry") runTranscriptAction({ act: "retry" });
|
|
975
|
+
else if (act === "switch-model") runTranscriptAction({ act: "switch-model" });
|
|
976
|
+
else if (act === "copy-error" && this.#errorText) runTranscriptAction({ act: "copy", text: this.#errorText });
|
|
977
|
+
}
|
|
978
|
+
|
|
613
979
|
/** Width-independent stable identities for the streamed leading thinking run. */
|
|
614
980
|
getTranscriptStableRows(): readonly TranscriptStableRow[] {
|
|
615
981
|
return this.#transcriptStableRows;
|
|
@@ -626,22 +992,33 @@ export class AssistantMessageComponent extends Container {
|
|
|
626
992
|
this.#stableSnapshots = [];
|
|
627
993
|
this.#stableParts = [];
|
|
628
994
|
this.#transcriptStableRows = [];
|
|
629
|
-
this.#
|
|
995
|
+
this.#dropStableRenders();
|
|
630
996
|
}
|
|
631
997
|
|
|
632
998
|
renderTranscriptStableRows(count: number, width: number): readonly string[] {
|
|
633
999
|
const index = Math.min(Math.trunc(count), this.#stableSnapshots.length);
|
|
634
1000
|
if (index <= 0) return EMPTY_STABLE_RENDER;
|
|
1001
|
+
this.#syncStableRenderInputs();
|
|
1002
|
+
const ledger = this.#stableLedgers.get(width);
|
|
1003
|
+
if (ledger?.newest === index) return ledger.rows;
|
|
635
1004
|
const key = `${index}:${width}`;
|
|
636
1005
|
const cached = this.#stableRenderCache.get(key);
|
|
637
1006
|
if (cached) return cached;
|
|
638
|
-
const
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
1007
|
+
const end = ledger?.ends.get(index);
|
|
1008
|
+
let rows: readonly string[];
|
|
1009
|
+
if (ledger !== undefined && end !== undefined) {
|
|
1010
|
+
rows = ledger.rows.slice(0, end);
|
|
1011
|
+
} else {
|
|
1012
|
+
const snapshot = this.#stableSnapshots[index - 1]!;
|
|
1013
|
+
rows = this.#renderStableParts(
|
|
1014
|
+
this.#stableParts,
|
|
1015
|
+
snapshot.partCount,
|
|
1016
|
+
snapshot.lastTextLength,
|
|
1017
|
+
width,
|
|
1018
|
+
"replay",
|
|
1019
|
+
);
|
|
1020
|
+
if (this.#recordStableRows(index, width, rows)) return rows;
|
|
643
1021
|
}
|
|
644
|
-
const rows = this.#renderStableSnapshot(parts, width);
|
|
645
1022
|
this.#stableRenderCache.set(key, rows);
|
|
646
1023
|
return rows;
|
|
647
1024
|
}
|
|
@@ -662,9 +1039,12 @@ export class AssistantMessageComponent extends Container {
|
|
|
662
1039
|
if (!last || last.kind === "spacer") return;
|
|
663
1040
|
const snapshot = { partCount: parts.length, lastTextLength: last.text.length };
|
|
664
1041
|
const previous = this.#stableSnapshots.at(-1);
|
|
665
|
-
|
|
1042
|
+
// An unmoved boundary publishes nothing whether or not it still extends
|
|
1043
|
+
// the last snapshot, so most frames skip the whole-document comparison.
|
|
666
1044
|
if (previous?.partCount === snapshot.partCount && previous.lastTextLength === snapshot.lastTextLength) return;
|
|
667
|
-
|
|
1045
|
+
if (previous && !isSnapshotExtension(this.#stableParts, parts)) return;
|
|
1046
|
+
this.#syncStableRenderInputs();
|
|
1047
|
+
const currentRows = this.#renderStableParts(parts, parts.length, last.text.length, width, "head");
|
|
668
1048
|
// The container verifies stable rows against the blank-trimmed render.
|
|
669
1049
|
if (!isRowPrefix(currentRows, trimBlankEdges(rendered))) return;
|
|
670
1050
|
const previousRows = previous
|
|
@@ -676,7 +1056,7 @@ export class AssistantMessageComponent extends Container {
|
|
|
676
1056
|
this.#stableParts = parts;
|
|
677
1057
|
this.#stableSnapshots.push(snapshot);
|
|
678
1058
|
this.#transcriptStableRows.push({ key: `thinking:${this.#nextStableRowId++}` });
|
|
679
|
-
this.#
|
|
1059
|
+
this.#recordStableRows(this.#stableSnapshots.length, width, currentRows);
|
|
680
1060
|
}
|
|
681
1061
|
|
|
682
1062
|
/**
|
|
@@ -726,37 +1106,136 @@ export class AssistantMessageComponent extends Container {
|
|
|
726
1106
|
return parts;
|
|
727
1107
|
}
|
|
728
1108
|
|
|
729
|
-
|
|
1109
|
+
/**
|
|
1110
|
+
* Render the first `partCount` stable parts, the final one cut to
|
|
1111
|
+
* `lastLength`. Rows match fresh Markdown renders of each part byte for
|
|
1112
|
+
* byte — {@link #createStableMarkdown} mirrors the live children — but a
|
|
1113
|
+
* closed part renders once per width, and the growing final part reuses one
|
|
1114
|
+
* Markdown instance so its already-frozen blocks are not re-lexed.
|
|
1115
|
+
*/
|
|
1116
|
+
#renderStableParts(
|
|
1117
|
+
parts: readonly StablePart[],
|
|
1118
|
+
partCount: number,
|
|
1119
|
+
lastLength: number,
|
|
1120
|
+
width: number,
|
|
1121
|
+
role: "head" | "replay",
|
|
1122
|
+
): readonly string[] {
|
|
1123
|
+
const ledger = this.#stableLedger(width);
|
|
730
1124
|
const rows: string[] = [];
|
|
731
|
-
|
|
1125
|
+
const lastIndex = partCount - 1;
|
|
1126
|
+
for (let index = 0; index < partCount; index++) {
|
|
1127
|
+
const part = parts[index]!;
|
|
732
1128
|
if (part.kind === "spacer") {
|
|
733
1129
|
rows.push("");
|
|
734
1130
|
continue;
|
|
735
1131
|
}
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
this.#getProseTheme(),
|
|
748
|
-
this.#textColorTransform ? { color: this.#textColorTransform } : undefined,
|
|
749
|
-
0,
|
|
750
|
-
)
|
|
751
|
-
: new Markdown(text, 1, 0, getMarkdownTheme(), {
|
|
752
|
-
color: (value: string) => theme.fg("thinkingText", value),
|
|
753
|
-
italic: true,
|
|
754
|
-
});
|
|
755
|
-
rows.push(...markdown.render(width));
|
|
1132
|
+
const closed = index < lastIndex;
|
|
1133
|
+
const text = closed || lastLength === part.text.length ? part.text : part.text.slice(0, lastLength);
|
|
1134
|
+
const cached = ledger.parts[index];
|
|
1135
|
+
let partRows: readonly string[];
|
|
1136
|
+
if (cached?.kind === part.kind && cached.text === text) {
|
|
1137
|
+
partRows = cached.rows;
|
|
1138
|
+
} else {
|
|
1139
|
+
partRows = this.#renderStablePart(index, part.kind, text, width, closed ? undefined : role);
|
|
1140
|
+
if (closed) ledger.parts[index] = { kind: part.kind, text, rows: partRows };
|
|
1141
|
+
}
|
|
1142
|
+
for (const row of partRows) rows.push(row);
|
|
756
1143
|
}
|
|
757
1144
|
return rows;
|
|
758
1145
|
}
|
|
759
1146
|
|
|
1147
|
+
/**
|
|
1148
|
+
* Render one part's Markdown. `role` names the instance reused for a final
|
|
1149
|
+
* part as its text grows; a closed part continues whichever instance was
|
|
1150
|
+
* already growing it, else renders once. Finalized blocks keep no instance.
|
|
1151
|
+
*/
|
|
1152
|
+
#renderStablePart(
|
|
1153
|
+
index: number,
|
|
1154
|
+
kind: StablePartKind,
|
|
1155
|
+
text: string,
|
|
1156
|
+
width: number,
|
|
1157
|
+
role: "head" | "replay" | undefined,
|
|
1158
|
+
): readonly string[] {
|
|
1159
|
+
// Trim like the live children, dropping the trailing blank line a frozen
|
|
1160
|
+
// prefix still carries.
|
|
1161
|
+
const trimmed = text.trim();
|
|
1162
|
+
if (this.#transcriptBlockFinalized) return this.#createStableMarkdown(kind, trimmed).render(width);
|
|
1163
|
+
const head = this.#stableHeadRenderer;
|
|
1164
|
+
const replay = this.#stableReplayRenderer;
|
|
1165
|
+
const renderer = role === "head" ? head : role === "replay" ? replay : head?.index === index ? head : replay;
|
|
1166
|
+
if (renderer?.index === index && renderer.kind === kind) {
|
|
1167
|
+
renderer.md.setText(trimmed);
|
|
1168
|
+
return renderer.md.render(width);
|
|
1169
|
+
}
|
|
1170
|
+
const md = this.#createStableMarkdown(kind, trimmed);
|
|
1171
|
+
if (role === "head") this.#stableHeadRenderer = { index, kind, md };
|
|
1172
|
+
else if (role === "replay") this.#stableReplayRenderer = { index, kind, md };
|
|
1173
|
+
return md.render(width);
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
/** Constructor args mirror the live child Markdown so stable rows prefix the block render. */
|
|
1177
|
+
#createStableMarkdown(kind: StablePartKind, text: string): Markdown {
|
|
1178
|
+
return kind === "text"
|
|
1179
|
+
? new Markdown(
|
|
1180
|
+
text,
|
|
1181
|
+
1,
|
|
1182
|
+
0,
|
|
1183
|
+
this.#getProseTheme(),
|
|
1184
|
+
this.#textColorTransform ? { color: this.#textColorTransform } : undefined,
|
|
1185
|
+
0,
|
|
1186
|
+
)
|
|
1187
|
+
: new Markdown(text, 1, 0, getMarkdownTheme(), {
|
|
1188
|
+
color: (value: string) => theme.fg("thinkingText", value),
|
|
1189
|
+
italic: true,
|
|
1190
|
+
});
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
#stableLedger(width: number): StableRowLedger {
|
|
1194
|
+
let ledger = this.#stableLedgers.get(width);
|
|
1195
|
+
if (ledger === undefined) {
|
|
1196
|
+
ledger = { newest: 0, rows: EMPTY_STABLE_RENDER, ends: new Map(), parts: [] };
|
|
1197
|
+
this.#stableLedgers.set(width, ledger);
|
|
1198
|
+
}
|
|
1199
|
+
return ledger;
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
/**
|
|
1203
|
+
* Remember `rows` as snapshot `index`'s render at `width`. A newer snapshot
|
|
1204
|
+
* becomes the ledger's newest render — keeping earlier counts only when it
|
|
1205
|
+
* extends their rows; an older one that prefixes the newest keeps just its
|
|
1206
|
+
* row count. Returns whether `rows` is now the newest render.
|
|
1207
|
+
*/
|
|
1208
|
+
#recordStableRows(index: number, width: number, rows: readonly string[]): boolean {
|
|
1209
|
+
const ledger = this.#stableLedger(width);
|
|
1210
|
+
if (index > ledger.newest) {
|
|
1211
|
+
if (!isRowPrefix(ledger.rows, rows)) ledger.ends.clear();
|
|
1212
|
+
ledger.newest = index;
|
|
1213
|
+
ledger.rows = rows;
|
|
1214
|
+
ledger.ends.set(index, rows.length);
|
|
1215
|
+
return true;
|
|
1216
|
+
}
|
|
1217
|
+
if (index < ledger.newest && isRowPrefix(rows, ledger.rows)) ledger.ends.set(index, rows.length);
|
|
1218
|
+
return false;
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
/** Drop cached stable renders once the themes they were rendered with change. */
|
|
1222
|
+
#syncStableRenderInputs(): void {
|
|
1223
|
+
const prose = this.#getProseTheme();
|
|
1224
|
+
const markdown = getMarkdownTheme();
|
|
1225
|
+
const color = this.#textColorTransform;
|
|
1226
|
+
const inputs = this.#stableRenderInputs;
|
|
1227
|
+
if (inputs?.prose === prose && inputs.markdown === markdown && inputs.color === color) return;
|
|
1228
|
+
this.#dropStableRenders();
|
|
1229
|
+
this.#stableRenderInputs = { prose, markdown, color };
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
#dropStableRenders(): void {
|
|
1233
|
+
this.#stableLedgers.clear();
|
|
1234
|
+
this.#stableRenderCache.clear();
|
|
1235
|
+
this.#stableHeadRenderer = undefined;
|
|
1236
|
+
this.#stableReplayRenderer = undefined;
|
|
1237
|
+
}
|
|
1238
|
+
|
|
760
1239
|
/** Render completed prose rather than an earlier thinking row under emergency viewport pressure. */
|
|
761
1240
|
renderTranscriptBlockEmergencyRow(width: number): string | undefined {
|
|
762
1241
|
if (!this.#transcriptBlockFinalized) return undefined;
|
|
@@ -769,7 +1248,7 @@ export class AssistantMessageComponent extends Container {
|
|
|
769
1248
|
|
|
770
1249
|
markTranscriptBlockFinalized(): void {
|
|
771
1250
|
this.#transcriptBlockFinalized = true;
|
|
772
|
-
this.#
|
|
1251
|
+
this.#dropStableRenders();
|
|
773
1252
|
this.#stopThinkingAnimation();
|
|
774
1253
|
// If the live pulse was on screen when the block sealed, drop the fast path
|
|
775
1254
|
// and rebuild so the placeholder is removed — finalized blocks never animate.
|
|
@@ -914,6 +1393,8 @@ export class AssistantMessageComponent extends Container {
|
|
|
914
1393
|
}
|
|
915
1394
|
|
|
916
1395
|
#appendThinkingExtensions(contentIndex: number, thinkingIndex: number, text: string): void {
|
|
1396
|
+
const mounted: Component[] = [];
|
|
1397
|
+
this.#thinkingExtensions.set(contentIndex, mounted);
|
|
917
1398
|
for (const renderer of this.#thinkingRenderers) {
|
|
918
1399
|
try {
|
|
919
1400
|
const component = renderer(
|
|
@@ -927,6 +1408,7 @@ export class AssistantMessageComponent extends Container {
|
|
|
927
1408
|
);
|
|
928
1409
|
if (component) {
|
|
929
1410
|
this.#contentContainer.addChild(component);
|
|
1411
|
+
mounted.push(component);
|
|
930
1412
|
}
|
|
931
1413
|
} catch {
|
|
932
1414
|
// Ignore extension renderer failures and keep the original thinking block visible.
|
|
@@ -1046,6 +1528,7 @@ export class AssistantMessageComponent extends Container {
|
|
|
1046
1528
|
// Everything below renders the display form; #lastMessage keeps the
|
|
1047
1529
|
// verbatim message so re-renders re-derive the reaction deterministically.
|
|
1048
1530
|
message = this.#displayMessage(message, this.#lastUpdateTransient);
|
|
1531
|
+
this.#displayedMessage = message;
|
|
1049
1532
|
|
|
1050
1533
|
// Streaming-speed gauge: only a live, in-flight render of the single
|
|
1051
1534
|
// animating hidden-thinking block feeds the shared session tracker. The
|
|
@@ -1059,7 +1542,10 @@ export class AssistantMessageComponent extends Container {
|
|
|
1059
1542
|
// the gauge and pollute the next block. Providers that report usage only at
|
|
1060
1543
|
// turn end leave the live count flat, so the rate stays 0 and the badge
|
|
1061
1544
|
// self-suppresses (see #thinkingDotsLabel).
|
|
1062
|
-
|
|
1545
|
+
// Native terminals show the live rate on visible thinking too, not only the hidden pulse.
|
|
1546
|
+
const isThinkingNow =
|
|
1547
|
+
this.#lastUpdateTransient &&
|
|
1548
|
+
(isNativeRendering() ? this.#thinkingTailIndex(message) !== undefined : this.#shouldAnimateThinking(message));
|
|
1063
1549
|
if (isThinkingNow) {
|
|
1064
1550
|
const currentTokens = message.usage.reasoningTokens ?? message.usage.output;
|
|
1065
1551
|
this.#thinkingTokens = currentTokens;
|
|
@@ -1088,6 +1574,7 @@ export class AssistantMessageComponent extends Container {
|
|
|
1088
1574
|
|
|
1089
1575
|
// Clear content container
|
|
1090
1576
|
this.#contentContainer.clear();
|
|
1577
|
+
this.#thinkingExtensions.clear();
|
|
1091
1578
|
this.#emergencyText = undefined;
|
|
1092
1579
|
this.#thinkingDots = undefined;
|
|
1093
1580
|
this.#hasTruncatableError = false;
|