@linxiraos/pi-tui 1.1.26 → 1.2.0
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 +51 -491
- package/README.md +13 -7
- package/dist/types/active-terminal.d.ts +30 -0
- package/dist/types/app-keybindings.d.ts +7 -2
- package/dist/types/apps/cleanse-board.d.ts +1 -1
- package/dist/types/apps/debug/log-viewer.d.ts +4 -0
- package/dist/types/apps/debug/raw-sse-buffer.d.ts +2 -0
- package/dist/types/apps/git/git-tui.d.ts +5 -4
- package/dist/types/apps/git/help.d.ts +3 -1
- package/dist/types/apps/if-bench-board.d.ts +2 -2
- package/dist/types/apps/setup-model-picker.d.ts +1 -1
- package/dist/types/autocomplete.d.ts +5 -2
- package/dist/types/charts/chart-plan.d.ts +674 -0
- package/dist/types/charts/chart-svg.d.ts +59 -0
- package/dist/types/charts/table-data.d.ts +175 -0
- package/dist/types/chat/assistant-message.d.ts +11 -1
- package/dist/types/chat/chat-transcript-builder.d.ts +3 -0
- package/dist/types/chat/compaction-summary-message.d.ts +1 -0
- package/dist/types/chat/execution-shared.d.ts +6 -5
- package/dist/types/chat/fence-figure.d.ts +42 -0
- package/dist/types/chat/figure-markdown.d.ts +42 -0
- package/dist/types/chat/messages.d.ts +1 -1
- package/dist/types/chat/svg-figure.d.ts +33 -0
- package/dist/types/chat/svg-source.d.ts +49 -0
- package/dist/types/chat/table-chart.d.ts +89 -0
- package/dist/types/chat/tool-execution.d.ts +2 -0
- package/dist/types/chat/user-message.d.ts +17 -4
- package/dist/types/chrome/shared.d.ts +9 -0
- package/dist/types/chrome/status-notice.d.ts +15 -3
- package/dist/types/chrome/transcript-container.d.ts +6 -0
- package/dist/types/components/box.d.ts +1 -0
- package/dist/types/components/disclosure.d.ts +1 -0
- package/dist/types/components/editor.d.ts +23 -5
- package/dist/types/components/form.d.ts +1 -0
- package/dist/types/components/image.d.ts +26 -44
- package/dist/types/components/input.d.ts +2 -1
- package/dist/types/components/key-value-list.d.ts +1 -0
- package/dist/types/components/layout/row.d.ts +1 -0
- package/dist/types/components/layout/stack.d.ts +1 -0
- package/dist/types/components/loader.d.ts +5 -6
- package/dist/types/components/markdown.d.ts +47 -5
- package/dist/types/components/scroll-view.d.ts +1 -0
- package/dist/types/components/section.d.ts +1 -0
- package/dist/types/components/table.d.ts +1 -0
- package/dist/types/components/text.d.ts +1 -0
- package/dist/types/components/tree-view.d.ts +7 -0
- package/dist/types/components/truncated-text.d.ts +1 -0
- package/dist/types/fuzzy.d.ts +29 -1
- package/dist/types/key-hint-format.d.ts +9 -1
- package/dist/types/keybinding-matchers.d.ts +3 -0
- package/dist/types/keybindings.d.ts +1 -1
- package/dist/types/kitty-graphics.d.ts +2 -2
- package/dist/types/loop-watchdog.d.ts +8 -7
- package/dist/types/multiplexers/cmux.d.ts +16 -0
- package/dist/types/multiplexers/herdr.d.ts +26 -0
- package/dist/types/multiplexers/notify.d.ts +13 -0
- package/dist/types/multiplexers/orca.d.ts +12 -0
- package/dist/types/multiplexers/screen.d.ts +9 -0
- package/dist/types/multiplexers/tmux.d.ts +29 -0
- package/dist/types/multiplexers/types.d.ts +100 -0
- package/dist/types/multiplexers/wmux.d.ts +12 -0
- package/dist/types/multiplexers/zellij.d.ts +12 -0
- package/dist/types/native/apply.d.ts +4 -3
- package/dist/types/native/backend.d.ts +5 -4
- package/dist/types/native/blobs.d.ts +7 -3
- package/dist/types/native/encode.d.ts +8 -6
- package/dist/types/native/node.d.ts +36 -5
- package/dist/types/native/overlay.d.ts +8 -3
- package/dist/types/native/settle.d.ts +4 -3
- package/dist/types/overlays/agent-hub-projection.d.ts +6 -5
- package/dist/types/overlays/agent-hub-types.d.ts +9 -0
- package/dist/types/overlays/agent-hub.d.ts +3 -0
- package/dist/types/overlays/agent-transcript-viewer.d.ts +12 -2
- package/dist/types/overlays/agents-hub.d.ts +9 -0
- package/dist/types/overlays/annotation-overlay.d.ts +19 -2
- package/dist/types/overlays/annotation-types.d.ts +2 -0
- package/dist/types/overlays/ask-dialog.d.ts +5 -2
- package/dist/types/overlays/btw-history-panel.d.ts +13 -1
- package/dist/types/overlays/copy-selector.d.ts +1 -0
- package/dist/types/overlays/history-search.d.ts +10 -0
- package/dist/types/overlays/hook-selector.d.ts +9 -4
- package/dist/types/overlays/hub-frame.d.ts +4 -1
- package/dist/types/overlays/jobs-panel.d.ts +1 -7
- package/dist/types/overlays/model-browser.d.ts +54 -7
- package/dist/types/overlays/model-hub.d.ts +37 -0
- package/dist/types/overlays/oauth-selector.d.ts +0 -1
- package/dist/types/overlays/plan-review-overlay.d.ts +3 -2
- package/dist/types/overlays/report-panel.d.ts +68 -0
- package/dist/types/overlays/rewind-selector.d.ts +1 -0
- package/dist/types/overlays/session-observer-registry.d.ts +2 -0
- package/dist/types/overlays/session-selector.d.ts +6 -2
- package/dist/types/overlays/settings-defs.d.ts +1 -0
- package/dist/types/overlays/thinking-selector.d.ts +2 -2
- package/dist/types/overlays/usage-dashboard.d.ts +12 -8
- package/dist/types/overlays/usage-display.d.ts +3 -1
- package/dist/types/prompt/composer-cache.d.ts +0 -16
- package/dist/types/prompt/composer.d.ts +18 -3
- package/dist/types/prompt/custom-editor.d.ts +25 -13
- package/dist/types/prompt/image-source.d.ts +2 -1
- package/dist/types/prompt/interactive-context-helpers.d.ts +5 -1
- package/dist/types/prompt/model-mention-autocomplete.d.ts +1 -1
- package/dist/types/prompt/welcome.d.ts +7 -2
- package/dist/types/render/composite.d.ts +2 -0
- package/dist/types/render/output-pane.d.ts +1 -0
- package/dist/types/render/render-utils.d.ts +1 -0
- package/dist/types/render/tool-card.d.ts +6 -0
- package/dist/types/render/width-aware-text.d.ts +2 -0
- package/dist/types/setup/scenes/types.d.ts +1 -0
- package/dist/types/space-hold.d.ts +40 -25
- package/dist/types/status-line/component.d.ts +3 -0
- package/dist/types/status-line/context-usage.d.ts +7 -5
- package/dist/types/status-line/host.d.ts +4 -1
- package/dist/types/status-line/segments.d.ts +2 -0
- package/dist/types/status-line/types.d.ts +14 -3
- package/dist/types/terminal-capabilities.d.ts +41 -36
- package/dist/types/terminal-multiplexer.d.ts +127 -14
- package/dist/types/terminal.d.ts +23 -16
- package/dist/types/theme/active-symbols.d.ts +1 -1
- package/dist/types/theme/schema.d.ts +11 -2
- package/dist/types/theme/symbols.d.ts +1 -1
- package/dist/types/theme/theme-class.d.ts +7 -0
- package/dist/types/theme/theme.d.ts +1 -1
- package/dist/types/theme/tui-adapters.d.ts +3 -0
- package/dist/types/thinking.d.ts +1 -1
- package/dist/types/tmux.d.ts +11 -4
- package/dist/types/tools/bash.d.ts +7 -2
- package/dist/types/tools/daemon.d.ts +1 -1
- package/dist/types/tools/eval.d.ts +22 -3
- package/dist/types/tools/glob.d.ts +4 -0
- package/dist/types/tools/native-view.d.ts +47 -0
- package/dist/types/tools/output-meta.d.ts +11 -1
- package/dist/types/tools/renderer.d.ts +37 -2
- package/dist/types/tools/streaming-output.d.ts +20 -1
- package/dist/types/tools/task.d.ts +2 -3
- package/dist/types/tools/write.d.ts +9 -2
- package/dist/types/tools/yield.d.ts +22 -0
- package/dist/types/tui.d.ts +36 -0
- package/dist/types/utils.d.ts +6 -0
- package/dist/types/vim.d.ts +19 -5
- package/dist/types/windows-input-mode.d.ts +33 -1
- package/package.json +13 -9
- package/src/active-terminal.d.ts +30 -0
- package/src/active-terminal.ts +80 -0
- package/src/app-keybindings.d.ts +7 -2
- package/src/app-keybindings.ts +7 -2
- package/src/apps/autoresearch-dashboard.ts +7 -3
- package/src/apps/cleanse-board.ts +2 -2
- package/src/apps/cleanse-picker.ts +1 -1
- package/src/apps/debug/log-viewer.ts +188 -72
- package/src/apps/debug/raw-sse-buffer.ts +8 -0
- package/src/apps/debug/raw-sse.ts +39 -5
- package/src/apps/git/diff-pane.ts +96 -23
- package/src/apps/git/git-tui.ts +23 -16
- package/src/apps/git/help.ts +29 -20
- package/src/apps/git/sidebar.ts +8 -3
- package/src/apps/if-bench-board.ts +2 -2
- package/src/apps/ps-top.ts +2 -2
- package/src/apps/setup-model-picker.ts +1 -1
- package/src/autocomplete.d.ts +5 -2
- package/src/autocomplete.ts +67 -43
- package/src/bracketed-paste.ts +10 -3
- package/src/charts/chart-plan.d.ts +674 -0
- package/src/charts/chart-plan.ts +5081 -0
- package/src/charts/chart-svg.d.ts +59 -0
- package/src/charts/chart-svg.ts +4657 -0
- package/src/charts/table-data.d.ts +175 -0
- package/src/charts/table-data.ts +1478 -0
- package/src/chat/advisor-message.ts +11 -0
- package/src/chat/assistant-message.d.ts +11 -1
- package/src/chat/assistant-message.ts +245 -54
- package/src/chat/chat-transcript-builder.d.ts +3 -0
- package/src/chat/chat-transcript-builder.ts +9 -3
- package/src/chat/compaction-summary-message.d.ts +1 -0
- package/src/chat/compaction-summary-message.ts +8 -0
- package/src/chat/execution-shared.d.ts +6 -5
- package/src/chat/execution-shared.ts +42 -25
- package/src/chat/fence-figure.d.ts +42 -0
- package/src/chat/fence-figure.ts +80 -0
- package/src/chat/figure-markdown.d.ts +42 -0
- package/src/chat/figure-markdown.ts +174 -0
- package/src/chat/messages.d.ts +1 -1
- package/src/chat/messages.ts +1 -1
- package/src/chat/read-tool-group.ts +6 -4
- package/src/chat/skill-message.ts +6 -0
- package/src/chat/svg-figure.d.ts +33 -0
- package/src/chat/svg-figure.ts +399 -0
- package/src/chat/svg-source.d.ts +49 -0
- package/src/chat/svg-source.ts +188 -0
- package/src/chat/table-chart.d.ts +89 -0
- package/src/chat/table-chart.ts +294 -0
- package/src/chat/tool-execution.d.ts +2 -0
- package/src/chat/tool-execution.ts +86 -7
- package/src/chat/user-message.d.ts +17 -4
- package/src/chat/user-message.ts +71 -8
- package/src/chrome/shared.ts +53 -0
- package/src/chrome/status-notice.ts +25 -7
- package/src/chrome/transcript-container.d.ts +6 -0
- package/src/chrome/transcript-container.ts +162 -19
- package/src/components/box.d.ts +1 -0
- package/src/components/box.ts +5 -0
- package/src/components/disclosure.d.ts +1 -0
- package/src/components/disclosure.ts +8 -0
- package/src/components/editor.d.ts +23 -5
- package/src/components/editor.ts +316 -47
- package/src/components/form.d.ts +1 -0
- package/src/components/form.ts +8 -0
- package/src/components/image.d.ts +26 -44
- package/src/components/image.ts +115 -152
- package/src/components/input.d.ts +2 -1
- package/src/components/input.ts +17 -7
- package/src/components/key-value-list.d.ts +1 -0
- package/src/components/key-value-list.ts +4 -0
- package/src/components/layout/row.d.ts +1 -0
- package/src/components/layout/row.ts +7 -0
- package/src/components/layout/stack.d.ts +1 -0
- package/src/components/layout/stack.ts +7 -0
- package/src/components/loader.d.ts +5 -6
- package/src/components/loader.ts +10 -17
- package/src/components/markdown.d.ts +47 -5
- package/src/components/markdown.ts +786 -303
- package/src/components/scroll-view.d.ts +1 -0
- package/src/components/scroll-view.ts +5 -0
- package/src/components/section.d.ts +1 -0
- package/src/components/section.ts +5 -0
- package/src/components/settings-list.ts +2 -0
- package/src/components/table.d.ts +1 -0
- package/src/components/table.ts +4 -0
- package/src/components/text.d.ts +1 -0
- package/src/components/text.ts +7 -0
- package/src/components/tree-view.d.ts +7 -0
- package/src/components/tree-view.ts +38 -8
- package/src/components/truncated-text.d.ts +1 -0
- package/src/components/truncated-text.ts +5 -0
- package/src/desktop-notify.ts +3 -2
- package/src/fuzzy.d.ts +29 -1
- package/src/fuzzy.ts +115 -30
- package/src/hotkeys-markdown.ts +9 -2
- package/src/key-hint-format.d.ts +9 -1
- package/src/key-hint-format.ts +26 -11
- package/src/keybinding-matchers.d.ts +3 -0
- package/src/keybinding-matchers.ts +6 -2
- package/src/keybindings.d.ts +1 -1
- package/src/keybindings.ts +1 -1
- package/src/kitty-graphics.d.ts +2 -2
- package/src/kitty-graphics.ts +11 -5
- package/src/loop-watchdog.d.ts +8 -7
- package/src/loop-watchdog.ts +18 -13
- package/src/multiplexers/cmux.d.ts +16 -0
- package/src/multiplexers/cmux.ts +29 -0
- package/src/multiplexers/herdr.d.ts +26 -0
- package/src/multiplexers/herdr.ts +64 -0
- package/src/multiplexers/notify.d.ts +13 -0
- package/src/multiplexers/notify.ts +24 -0
- package/src/multiplexers/orca.d.ts +12 -0
- package/src/multiplexers/orca.ts +16 -0
- package/src/multiplexers/screen.d.ts +9 -0
- package/src/multiplexers/screen.ts +15 -0
- package/src/multiplexers/tmux.d.ts +29 -0
- package/src/multiplexers/tmux.ts +40 -0
- package/src/multiplexers/types.d.ts +100 -0
- package/src/multiplexers/types.ts +110 -0
- package/src/multiplexers/wmux.d.ts +12 -0
- package/src/multiplexers/wmux.ts +16 -0
- package/src/multiplexers/zellij.d.ts +12 -0
- package/src/multiplexers/zellij.ts +24 -0
- package/src/native/apply.d.ts +4 -3
- package/src/native/apply.ts +4 -3
- package/src/native/backend.d.ts +5 -4
- package/src/native/backend.ts +199 -22
- package/src/native/blobs.d.ts +7 -3
- package/src/native/blobs.ts +47 -12
- package/src/native/encode.d.ts +8 -6
- package/src/native/encode.ts +76 -34
- package/src/native/node.d.ts +36 -5
- package/src/native/node.ts +31 -5
- package/src/native/overlay.d.ts +8 -3
- package/src/native/overlay.ts +17 -4
- package/src/native/reconcile.ts +16 -4
- package/src/native/settle.d.ts +4 -3
- package/src/native/settle.ts +4 -3
- package/src/overlays/agent-hub-projection.d.ts +6 -5
- package/src/overlays/agent-hub-projection.ts +124 -13
- package/src/overlays/agent-hub-types.d.ts +9 -0
- package/src/overlays/agent-hub-types.ts +6 -0
- package/src/overlays/agent-hub.ts +48 -29
- package/src/overlays/agent-transcript-viewer.ts +93 -23
- package/src/overlays/agents-hub.ts +28 -8
- package/src/overlays/annotation-overlay.ts +293 -90
- package/src/overlays/annotation-types.ts +2 -0
- package/src/overlays/ask-dialog.ts +10 -17
- package/src/overlays/btw-history-panel.ts +136 -39
- package/src/overlays/copy-selector.d.ts +1 -0
- package/src/overlays/copy-selector.ts +32 -8
- package/src/overlays/extensions/extension-dashboard.ts +4 -4
- package/src/overlays/extensions/state-manager.ts +52 -12
- package/src/overlays/history-search.ts +100 -7
- package/src/overlays/hook-selector.ts +12 -5
- package/src/overlays/hub-frame.ts +19 -1
- package/src/overlays/jobs-panel.ts +33 -52
- package/src/overlays/model-browser.ts +367 -122
- package/src/overlays/model-hub.ts +435 -59
- package/src/overlays/move-overlay.ts +28 -14
- package/src/overlays/oauth-selector.ts +54 -70
- package/src/overlays/pause-screen.ts +2 -2
- package/src/overlays/plan-review-overlay.ts +208 -70
- package/src/overlays/plugin-settings.ts +3 -3
- package/src/overlays/report-panel.ts +191 -0
- package/src/overlays/rewind-selector.ts +88 -17
- package/src/overlays/session-observer-registry.d.ts +2 -0
- package/src/overlays/session-observer-registry.ts +10 -2
- package/src/overlays/session-selector.ts +64 -26
- package/src/overlays/settings-defs.ts +5 -4
- package/src/overlays/settings-selector.ts +33 -8
- package/src/overlays/snapcompact-shape-preview.ts +2 -3
- package/src/overlays/stats-notice.ts +1 -1
- package/src/overlays/thinking-selector.ts +11 -10
- package/src/overlays/tree-selector.ts +41 -20
- package/src/overlays/usage-dashboard.ts +117 -58
- package/src/overlays/usage-display.ts +10 -4
- package/src/overlays/usage-row.ts +22 -8
- package/src/prompt/attachment-chips.ts +15 -11
- package/src/prompt/composer-attachments.ts +6 -4
- package/src/prompt/composer-cache.ts +7 -86
- package/src/prompt/composer.ts +37 -26
- package/src/prompt/custom-editor.ts +282 -105
- package/src/prompt/image-source.ts +2 -1
- package/src/prompt/interactive-context-helpers.ts +32 -18
- package/src/prompt/macos-spelling.ts +7 -0
- package/src/prompt/magic-keywords.ts +11 -2
- package/src/prompt/model-mention-autocomplete.ts +14 -14
- package/src/prompt/queued-messages.ts +6 -4
- package/src/prompt/tips.txt +13 -9
- package/src/prompt/welcome.ts +64 -46
- package/src/prompt/word-completion.ts +38 -5
- package/src/render/composite.d.ts +2 -0
- package/src/render/composite.ts +64 -0
- package/src/render/output-pane.d.ts +1 -0
- package/src/render/output-pane.ts +6 -0
- package/src/render/render-utils.d.ts +1 -0
- package/src/render/render-utils.ts +16 -1
- package/src/render/tool-card.d.ts +6 -0
- package/src/render/tool-card.ts +15 -0
- package/src/render/width-aware-text.d.ts +2 -0
- package/src/render/width-aware-text.ts +5 -0
- package/src/setup/scenes/model.ts +2 -1
- package/src/setup/scenes/types.ts +1 -0
- package/src/space-hold.d.ts +40 -25
- package/src/space-hold.ts +156 -52
- package/src/status-line/component.ts +292 -78
- package/src/status-line/context-usage.ts +21 -46
- package/src/status-line/host.ts +4 -1
- package/src/status-line/segments.ts +40 -22
- package/src/status-line/types.ts +15 -3
- package/src/terminal-capabilities.d.ts +41 -36
- package/src/terminal-capabilities.ts +137 -213
- package/src/terminal-multiplexer.d.ts +127 -14
- package/src/terminal-multiplexer.ts +112 -33
- package/src/terminal.d.ts +23 -16
- package/src/terminal.ts +203 -99
- package/src/theme/active-symbols.d.ts +1 -1
- package/src/theme/active-symbols.ts +1 -1
- package/src/theme/schema-validation.ts +1 -0
- package/src/theme/schema.d.ts +11 -2
- package/src/theme/schema.ts +14 -1
- package/src/theme/session-color.ts +1 -7
- package/src/theme/symbols.d.ts +1 -1
- package/src/theme/symbols.ts +11 -4
- package/src/theme/theme-class.d.ts +7 -0
- package/src/theme/theme-class.ts +26 -1
- package/src/theme/theme-schema.json +5 -1
- package/src/theme/theme.d.ts +1 -1
- package/src/theme/theme.ts +8 -4
- package/src/theme/tui-adapters.d.ts +3 -0
- package/src/theme/tui-adapters.ts +68 -0
- package/src/thinking.d.ts +1 -1
- package/src/thinking.ts +1 -1
- package/src/tmux.d.ts +11 -4
- package/src/tmux.ts +47 -11
- package/src/tools/ast-edit.ts +59 -35
- package/src/tools/ast-grep.ts +35 -16
- package/src/tools/bash.d.ts +7 -2
- package/src/tools/bash.ts +116 -47
- package/src/tools/daemon.d.ts +1 -1
- package/src/tools/daemon.ts +1 -1
- package/src/tools/default-renderer.ts +35 -19
- package/src/tools/edit.ts +142 -120
- package/src/tools/eval-format/index.ts +28 -2
- package/src/tools/eval-format/javascript.ts +7 -5
- package/src/tools/eval.d.ts +22 -3
- package/src/tools/eval.ts +364 -208
- package/src/tools/fetch.ts +5 -6
- package/src/tools/glob.d.ts +4 -0
- package/src/tools/glob.ts +33 -65
- package/src/tools/grep.ts +81 -48
- package/src/tools/index.ts +2 -0
- package/src/tools/lsp.ts +81 -59
- package/src/tools/mcp.ts +111 -68
- package/src/tools/native-view.d.ts +47 -0
- package/src/tools/native-view.ts +102 -1
- package/src/tools/output-meta.d.ts +11 -1
- package/src/tools/output-meta.ts +23 -3
- package/src/tools/read.ts +7 -5
- package/src/tools/renderer.d.ts +37 -2
- package/src/tools/renderer.ts +42 -2
- package/src/tools/streaming-output.d.ts +20 -1
- package/src/tools/streaming-output.ts +154 -50
- package/src/tools/task.d.ts +2 -3
- package/src/tools/task.ts +115 -62
- package/src/tools/todo.ts +119 -30
- package/src/tools/wait.ts +34 -24
- package/src/tools/web-search.ts +1 -1
- package/src/tools/write.d.ts +9 -2
- package/src/tools/write.ts +147 -95
- package/src/tools/yield.d.ts +22 -0
- package/src/tools/yield.ts +225 -0
- package/src/tui.d.ts +36 -0
- package/src/tui.ts +666 -217
- package/src/utils.d.ts +6 -0
- package/src/utils.ts +19 -0
- package/src/vim.d.ts +19 -5
- package/src/vim.ts +333 -18
- package/src/windows-input-mode.d.ts +33 -1
- package/src/windows-input-mode.ts +212 -6
|
@@ -52,6 +52,10 @@ class AdvisorHeader implements Component {
|
|
|
52
52
|
this.#uiTheme = uiTheme;
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
releaseRenderCaches(): void {
|
|
56
|
+
this.#cache = undefined;
|
|
57
|
+
}
|
|
58
|
+
|
|
55
59
|
invalidate(): void {
|
|
56
60
|
this.#cache = undefined;
|
|
57
61
|
}
|
|
@@ -82,6 +86,10 @@ class AdvisorNotes implements Component {
|
|
|
82
86
|
this.#uiTheme = uiTheme;
|
|
83
87
|
}
|
|
84
88
|
|
|
89
|
+
releaseRenderCaches(): void {
|
|
90
|
+
this.#cache = undefined;
|
|
91
|
+
}
|
|
92
|
+
|
|
85
93
|
invalidate(): void {
|
|
86
94
|
this.#cache = undefined;
|
|
87
95
|
}
|
|
@@ -236,6 +244,9 @@ export function createAdvisorMessageCard(
|
|
|
236
244
|
invalidate(): void {
|
|
237
245
|
disclosure.invalidate();
|
|
238
246
|
},
|
|
247
|
+
releaseRenderCaches(): void {
|
|
248
|
+
disclosure.releaseRenderCaches();
|
|
249
|
+
},
|
|
239
250
|
dispose(): void {
|
|
240
251
|
disclosure.dispose();
|
|
241
252
|
},
|
|
@@ -38,7 +38,7 @@ export declare class AssistantMessageComponent extends Container {
|
|
|
38
38
|
* for lack of a target is re-rendered with its reaction stripped.
|
|
39
39
|
*/
|
|
40
40
|
pickReactionTarget(transcript: readonly Component[]): void;
|
|
41
|
-
constructor(message?: AssistantMessage, hideThinkingBlock?: boolean, onImageUpdate?: () => void, thinkingRenderers?: readonly AssistantThinkingRenderer[], imageBudget?: ImageBudget, proseOnlyThinking?: boolean, linkTargets?: ReadonlyMap<string, string
|
|
41
|
+
constructor(message?: AssistantMessage, hideThinkingBlock?: boolean, onImageUpdate?: () => void, thinkingRenderers?: readonly AssistantThinkingRenderer[], imageBudget?: ImageBudget, proseOnlyThinking?: boolean, linkTargets?: ReadonlyMap<string, string>, expandThinkingBlocks?: boolean);
|
|
42
42
|
/**
|
|
43
43
|
* Show or clear the trailing cache-invalidation divider. Set at `message_end`
|
|
44
44
|
* (live) or during rebuild, once the turn's usage is known and compared
|
|
@@ -54,6 +54,7 @@ export declare class AssistantMessageComponent extends Container {
|
|
|
54
54
|
*/
|
|
55
55
|
setServedModelMismatch(info: ServedModelMismatch | undefined): void;
|
|
56
56
|
invalidate(): void;
|
|
57
|
+
releaseRenderCaches(): void;
|
|
57
58
|
setHideThinkingBlock(hide: boolean): void;
|
|
58
59
|
/**
|
|
59
60
|
* Allow or withhold retiring finished lines into native scrollback while the
|
|
@@ -65,6 +66,11 @@ export declare class AssistantMessageComponent extends Container {
|
|
|
65
66
|
*/
|
|
66
67
|
setMidStreamPublication(allowed: boolean): void;
|
|
67
68
|
setProseOnlyThinking(proseOnly: boolean): void;
|
|
69
|
+
/**
|
|
70
|
+
* Keep finished thinking sections expanded instead of folding them to "Thought for 12s".
|
|
71
|
+
* Sections the user folded or unfolded by hand keep that choice.
|
|
72
|
+
*/
|
|
73
|
+
setExpandThinkingBlocks(expand: boolean): void;
|
|
68
74
|
dispose(): void;
|
|
69
75
|
/**
|
|
70
76
|
* Toggle suppression of the inline `Error: …` line while the same error is
|
|
@@ -81,6 +87,8 @@ export declare class AssistantMessageComponent extends Container {
|
|
|
81
87
|
*/
|
|
82
88
|
setExpanded(expanded: boolean): void;
|
|
83
89
|
isTranscriptBlockFinalized(): boolean;
|
|
90
|
+
/** Whether a figure still waits for the raster of its final source; the transcript holds retirement meanwhile. */
|
|
91
|
+
isTranscriptBlockPending(): boolean;
|
|
84
92
|
render(width: number): readonly string[];
|
|
85
93
|
/**
|
|
86
94
|
* A `col` (role `zeta.assistant`) of `md` nodes keyed by content index, so
|
|
@@ -118,6 +126,8 @@ export declare class AssistantMessageComponent extends Container {
|
|
|
118
126
|
messagePersistenceKey(): string | undefined;
|
|
119
127
|
/** Toggle rendering for assistant-native and tool-result images. */
|
|
120
128
|
setImagesVisible(visible: boolean): void;
|
|
129
|
+
/** Toggle charts under numeric tables (the main session's transcript only, never a subagent's). */
|
|
130
|
+
setTableChartsVisible(visible: boolean): void;
|
|
121
131
|
/** Toggle only images produced by tool results; assistant-native images remain governed by setImagesVisible. */
|
|
122
132
|
setToolResultImagesVisible(visible: boolean): void;
|
|
123
133
|
setToolResultImages(toolCallId: string, images: ImageContent[]): void;
|
|
@@ -2,14 +2,19 @@ import type { AssistantMessage, ImageContent, TextContent } from "@linxiraos/pi-
|
|
|
2
2
|
import { type Component, Container } from "../tui";
|
|
3
3
|
import { Image, type ImageBudget } from "../components/image";
|
|
4
4
|
import { ImageProtocol, TERMINAL } from "../terminal-capabilities";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
type DefaultTextStyle,
|
|
7
|
+
Markdown,
|
|
8
|
+
type MarkdownTheme,
|
|
9
|
+
rewriteMarkdownLinkDestinations,
|
|
10
|
+
} from "../components/markdown";
|
|
6
11
|
import { Spacer } from "../components/spacer";
|
|
7
12
|
import { Text } from "../components/text";
|
|
8
13
|
import { formatDuration, formatNumber } from "@linxiraos/pi-utils";
|
|
9
14
|
import chalk from "@linxiraos/pi-utils/chalk";
|
|
10
15
|
import { LRUCache } from "@linxiraos/pi-utils/lru";
|
|
11
16
|
import type { AssistantThinkingRenderer } from "./extension-types";
|
|
12
|
-
import { ensureThemeSync, getMarkdownTheme, getThemeEpoch, theme } from "../theme";
|
|
17
|
+
import { ensureThemeSync, getMarkdownTheme, getMarkdownThemeWithLinkTargets, getThemeEpoch, theme } from "../theme";
|
|
13
18
|
import { card, col, elapsed, node, span, text } from "../native/describe";
|
|
14
19
|
import { hasTranscriptActions, runTranscriptAction } from "./transcript-actions";
|
|
15
20
|
import type { NativeChild, NativeNode, NativeUiEvent } from "../native/node";
|
|
@@ -27,6 +32,10 @@ import { type ServedModelMismatch, ServedModelMarkerComponent } from "./served-m
|
|
|
27
32
|
import { isReactionTarget, type ReactionSplit, type ReactionTarget, splitReaction } from "./reaction";
|
|
28
33
|
import { isRowPrefix, type TranscriptStableRow, trimBlankEdges } from "../chrome/transcript-container";
|
|
29
34
|
import { formatTurnUsage, type TurnUsageSummary } from "../overlays/usage-row";
|
|
35
|
+
import { FigureMarkdown } from "./figure-markdown";
|
|
36
|
+
import { svgFigureRendering } from "./svg-figure";
|
|
37
|
+
import { hasSvgFence } from "./svg-source";
|
|
38
|
+
import { describeTableChart, hasChartTable, lookupTableChart, splitTableCharts } from "./table-chart";
|
|
30
39
|
|
|
31
40
|
/**
|
|
32
41
|
* Max wrapped rows of a turn-ending provider error rendered inline in the
|
|
@@ -46,6 +55,15 @@ function thoughtLabel(clock: { start: number; end?: number } | undefined): strin
|
|
|
46
55
|
}
|
|
47
56
|
|
|
48
57
|
type ThinkingContentBlock = Extract<AssistantMessage["content"][number], { type: "thinking" }>;
|
|
58
|
+
/** Renders one text or thinking block: Markdown, or {@link FigureMarkdown} for text holding a ```svg fence. */
|
|
59
|
+
type ProseBlock = Markdown | FigureMarkdown;
|
|
60
|
+
/** A streamed block the fast path updates in place. */
|
|
61
|
+
interface FastPathItem {
|
|
62
|
+
md: ProseBlock;
|
|
63
|
+
contentIndex: number;
|
|
64
|
+
blockType: "text" | "thinking";
|
|
65
|
+
lastText: string;
|
|
66
|
+
}
|
|
49
67
|
type DisplayThinkingContentBlock = ThinkingContentBlock & { rawThinking?: string };
|
|
50
68
|
type StablePartKind = "thinking" | "text";
|
|
51
69
|
type StablePart = { kind: StablePartKind; text: string } | { kind: "spacer" };
|
|
@@ -230,22 +248,30 @@ export class AssistantMessageComponent extends Container {
|
|
|
230
248
|
#cacheMarker?: CacheInvalidationMarkerComponent;
|
|
231
249
|
#servedModelMarker?: ServedModelMarkerComponent;
|
|
232
250
|
#lastMessage?: AssistantMessage;
|
|
233
|
-
#emergencyText?:
|
|
251
|
+
#emergencyText?: ProseBlock;
|
|
234
252
|
#toolImagesByCallId = new Map<string, ImageContent[]>();
|
|
235
253
|
/**
|
|
236
254
|
* Payload keys ({@link imagePayloadKey}) whose Kitty PNG conversion this
|
|
237
|
-
* component
|
|
255
|
+
* component is awaiting, so a re-delivered image neither re-encodes nor
|
|
238
256
|
* schedules a second {@link updateContent} cascade. The conversions
|
|
239
|
-
* themselves
|
|
257
|
+
* themselves live in the bounded process-wide cache behind
|
|
258
|
+
* {@link convertImageToPngShared}; an evicted one is redone on the next
|
|
259
|
+
* render instead of being pinned here for the session.
|
|
240
260
|
*/
|
|
241
261
|
#kittyConversionsAwaited = new Set<string>();
|
|
242
262
|
/**
|
|
243
|
-
* Conversions
|
|
244
|
-
*
|
|
263
|
+
* Conversions the current image children display, by payload key. Rebuilt
|
|
264
|
+
* on every full render pass ({@link updateContent}), so a conversion is held
|
|
265
|
+
* only while it is on screen — a re-render after the shared cache evicted
|
|
266
|
+
* it still finds it — and released once hidden or replaced.
|
|
245
267
|
*/
|
|
246
|
-
#
|
|
268
|
+
#kittyDisplayed = new Map<string, ImageContent>();
|
|
269
|
+
/** The previous pass's {@link #kittyDisplayed}, readable only during a render pass. */
|
|
270
|
+
#kittyPreviouslyDisplayed: Map<string, ImageContent> | undefined;
|
|
247
271
|
#showImages = true;
|
|
248
272
|
#showToolResultImages = true;
|
|
273
|
+
/** Charts under numeric tables; off for subagent transcripts. */
|
|
274
|
+
#showTableCharts = true;
|
|
249
275
|
#transcriptBlockFinalized: boolean;
|
|
250
276
|
/** See {@link setMidStreamPublication}; the wire's `stream-revision` axis decides it. */
|
|
251
277
|
#midStreamPublication = true;
|
|
@@ -293,9 +319,13 @@ export class AssistantMessageComponent extends Container {
|
|
|
293
319
|
#lastUpdateTransient = false;
|
|
294
320
|
// Fast-path state: reuse Markdown children when message shape is stable during streaming.
|
|
295
321
|
#fastPathKey: string | undefined;
|
|
296
|
-
#fastPathItems:
|
|
297
|
-
|
|
298
|
-
|
|
322
|
+
#fastPathItems: FastPathItem[] | undefined;
|
|
323
|
+
/**
|
|
324
|
+
* Text blocks whose ```svg fences are lifted into figures, by content index.
|
|
325
|
+
* Kept across slow-path rebuilds so a figure keeps its raster (and its
|
|
326
|
+
* in-flight one) instead of starting over on every update.
|
|
327
|
+
*/
|
|
328
|
+
#figureBlocks = new Map<number, FigureMarkdown>();
|
|
299
329
|
/** Live "thinking" pulse shown in place of a hidden thinking block while it
|
|
300
330
|
* streams; undefined when not animating. Driven by {@link #thinkingDotsTimer}. */
|
|
301
331
|
#thinkingDots: Text | undefined;
|
|
@@ -339,6 +369,8 @@ export class AssistantMessageComponent extends Container {
|
|
|
339
369
|
#textColorTransform?: (text: string) => string;
|
|
340
370
|
#linkTargets: ReadonlyMap<string, string> = EMPTY_LINK_TARGETS;
|
|
341
371
|
#markdownTheme: MarkdownTheme | undefined;
|
|
372
|
+
/** Text-block sources with {@link #linkTargets} applied, for the native `md` nodes; reset with the targets. */
|
|
373
|
+
#nativeLinkSources = new Map<string, string>();
|
|
342
374
|
/** Block this reply reacts to; undefined when the preceding block takes no reactions. */
|
|
343
375
|
#reactionTarget: ReactionTarget | undefined;
|
|
344
376
|
/** Reaction lifted from the reply's opening emoji, once resolved. */
|
|
@@ -356,16 +388,38 @@ export class AssistantMessageComponent extends Container {
|
|
|
356
388
|
/** Markdown nodes by key, reused while their text and streaming flag are unchanged. */
|
|
357
389
|
#nativeParts = new Map<string, { text: string; stream: boolean; node: NativeNode }>();
|
|
358
390
|
readonly #nativeImages = new NativeImageCache();
|
|
391
|
+
/** Smart table chart picks a TSP describe is waiting on. */
|
|
392
|
+
readonly #chartPicks = new Set<Promise<void>>();
|
|
359
393
|
|
|
360
394
|
setTextColorTransform(transform?: (text: string) => string): void {
|
|
361
395
|
this.#textColorTransform = transform;
|
|
362
396
|
}
|
|
363
397
|
|
|
398
|
+
/**
|
|
399
|
+
* Default text style for assistant paragraph Markdown. An explicitly
|
|
400
|
+
* installed transform (live-command output) always wins and keeps its
|
|
401
|
+
* historical paint-everything contract; otherwise the optional
|
|
402
|
+
* `assistantMessageText` theme token paints prose, flagged `proseFg` so
|
|
403
|
+
* markdown sub-elements keep their own `md*` tokens (docs/theme.md).
|
|
404
|
+
* Returns undefined when the token is unset or empty so the terminal
|
|
405
|
+
* default foreground is used byte-for-byte, exactly as before the token
|
|
406
|
+
* existed.
|
|
407
|
+
*
|
|
408
|
+
* The token is re-derived on every rebuild (the theme-change path calls
|
|
409
|
+
* `invalidate()` and rebuilds through the teardown path, which re-consults
|
|
410
|
+
* the active theme), so prose follows theme switches both ways.
|
|
411
|
+
*/
|
|
412
|
+
#getProseTextStyle(): DefaultTextStyle | undefined {
|
|
413
|
+
if (this.#textColorTransform) return { color: this.#textColorTransform };
|
|
414
|
+
if (typeof theme === "undefined" || !theme.hasColor("assistantMessageText")) return undefined;
|
|
415
|
+
return { color: (text: string) => theme.fg("assistantMessageText", text), proseFg: true };
|
|
416
|
+
}
|
|
417
|
+
|
|
364
418
|
#getProseTheme(): MarkdownTheme {
|
|
365
419
|
if (this.#markdownTheme) return this.#markdownTheme;
|
|
366
420
|
const base = getMarkdownTheme();
|
|
367
421
|
const snapshot = this.#linkTargets;
|
|
368
|
-
const markdownTheme = snapshot.size > 0 ?
|
|
422
|
+
const markdownTheme = snapshot.size > 0 ? getMarkdownThemeWithLinkTargets(snapshot) : base;
|
|
369
423
|
this.#markdownTheme = markdownTheme;
|
|
370
424
|
return markdownTheme;
|
|
371
425
|
}
|
|
@@ -387,6 +441,9 @@ export class AssistantMessageComponent extends Container {
|
|
|
387
441
|
this.#markdownTheme = undefined;
|
|
388
442
|
this.#fastPathKey = undefined;
|
|
389
443
|
this.#fastPathItems = undefined;
|
|
444
|
+
for (const block of this.#figureBlocks.values()) block.restyle();
|
|
445
|
+
this.#nativeLinkSources.clear();
|
|
446
|
+
this.#nativeViewVersion++;
|
|
390
447
|
if (this.#lastMessage) {
|
|
391
448
|
this.updateContent(this.#lastMessage, { transient: this.#lastUpdateTransient });
|
|
392
449
|
}
|
|
@@ -456,6 +513,7 @@ export class AssistantMessageComponent extends Container {
|
|
|
456
513
|
readonly #thinkingRenderers: readonly AssistantThinkingRenderer[];
|
|
457
514
|
readonly #imageBudget?: ImageBudget;
|
|
458
515
|
#proseOnlyThinking: boolean;
|
|
516
|
+
#expandThinkingBlocks: boolean;
|
|
459
517
|
|
|
460
518
|
constructor(
|
|
461
519
|
message?: AssistantMessage,
|
|
@@ -465,6 +523,7 @@ export class AssistantMessageComponent extends Container {
|
|
|
465
523
|
imageBudget?: ImageBudget,
|
|
466
524
|
proseOnlyThinking = true,
|
|
467
525
|
linkTargets?: ReadonlyMap<string, string>,
|
|
526
|
+
expandThinkingBlocks = false,
|
|
468
527
|
) {
|
|
469
528
|
super();
|
|
470
529
|
this.#hideThinkingBlock = hideThinkingBlock;
|
|
@@ -472,6 +531,7 @@ export class AssistantMessageComponent extends Container {
|
|
|
472
531
|
this.#thinkingRenderers = thinkingRenderers;
|
|
473
532
|
this.#imageBudget = imageBudget;
|
|
474
533
|
this.#proseOnlyThinking = proseOnlyThinking;
|
|
534
|
+
this.#expandThinkingBlocks = expandThinkingBlocks;
|
|
475
535
|
|
|
476
536
|
ensureThemeSync();
|
|
477
537
|
this.#transcriptBlockFinalized = message !== undefined;
|
|
@@ -534,11 +594,18 @@ export class AssistantMessageComponent extends Container {
|
|
|
534
594
|
this.#markdownTheme = undefined;
|
|
535
595
|
this.#fastPathKey = undefined;
|
|
536
596
|
this.#fastPathItems = undefined;
|
|
597
|
+
for (const block of this.#figureBlocks.values()) block.restyle();
|
|
537
598
|
if (this.#lastMessage) {
|
|
538
599
|
this.updateContent(this.#lastMessage, { transient: this.#lastUpdateTransient });
|
|
539
600
|
}
|
|
540
601
|
}
|
|
541
602
|
|
|
603
|
+
override releaseRenderCaches(): void {
|
|
604
|
+
super.releaseRenderCaches();
|
|
605
|
+
this.#dropStableRenders();
|
|
606
|
+
this.#emergencyText?.releaseRenderCaches();
|
|
607
|
+
}
|
|
608
|
+
|
|
542
609
|
setHideThinkingBlock(hide: boolean): void {
|
|
543
610
|
this.#hideThinkingBlock = hide;
|
|
544
611
|
this.#thinkingCollapsed.clear();
|
|
@@ -560,6 +627,16 @@ export class AssistantMessageComponent extends Container {
|
|
|
560
627
|
this.#proseOnlyThinking = proseOnly;
|
|
561
628
|
}
|
|
562
629
|
|
|
630
|
+
/**
|
|
631
|
+
* Keep finished thinking sections expanded instead of folding them to "Thought for 12s".
|
|
632
|
+
* Sections the user folded or unfolded by hand keep that choice.
|
|
633
|
+
*/
|
|
634
|
+
setExpandThinkingBlocks(expand: boolean): void {
|
|
635
|
+
if (this.#expandThinkingBlocks === expand) return;
|
|
636
|
+
this.#expandThinkingBlocks = expand;
|
|
637
|
+
this.#nativeViewVersion++;
|
|
638
|
+
}
|
|
639
|
+
|
|
563
640
|
override dispose(): void {
|
|
564
641
|
this.#stopThinkingAnimation();
|
|
565
642
|
super.dispose();
|
|
@@ -700,6 +777,12 @@ export class AssistantMessageComponent extends Container {
|
|
|
700
777
|
return this.#transcriptBlockFinalized;
|
|
701
778
|
}
|
|
702
779
|
|
|
780
|
+
/** Whether a figure still waits for the raster of its final source; the transcript holds retirement meanwhile. */
|
|
781
|
+
isTranscriptBlockPending(): boolean {
|
|
782
|
+
for (const block of this.#figureBlocks.values()) if (block.pending) return true;
|
|
783
|
+
return false;
|
|
784
|
+
}
|
|
785
|
+
|
|
703
786
|
override render(width: number): readonly string[] {
|
|
704
787
|
const rows = super.render(width);
|
|
705
788
|
this.#publishStableSnapshot(rows, width);
|
|
@@ -793,7 +876,36 @@ export class AssistantMessageComponent extends Container {
|
|
|
793
876
|
const content = message.content[index]!;
|
|
794
877
|
const streaming = live && index === tailIndex;
|
|
795
878
|
if (content.type === "text" && canonicalizeMessage(content.text)) {
|
|
796
|
-
|
|
879
|
+
const source = content.text.trim();
|
|
880
|
+
// The terminal renders this source itself and would resolve a relative
|
|
881
|
+
// link against its own idea of the cwd: hand it the session-resolved
|
|
882
|
+
// targets (resolved once the segment closes, so never while streaming).
|
|
883
|
+
const linked = (text: string, live: boolean): string => {
|
|
884
|
+
if (live || this.#linkTargets.size === 0) return text;
|
|
885
|
+
const targets = this.#linkTargets;
|
|
886
|
+
const resolved =
|
|
887
|
+
this.#nativeLinkSources.get(text) ??
|
|
888
|
+
rewriteMarkdownLinkDestinations(text, href => targets.get(href));
|
|
889
|
+
this.#nativeLinkSources.set(text, resolved);
|
|
890
|
+
return resolved;
|
|
891
|
+
};
|
|
892
|
+
if (!this.#showImages || !this.#showTableCharts || !hasChartTable(source)) {
|
|
893
|
+
children.push(markdown(`t${index}`, `t${index}`, linked(source, streaming), streaming));
|
|
894
|
+
continue;
|
|
895
|
+
}
|
|
896
|
+
// A chart follows its table as an SVG image node; the prose splits around it.
|
|
897
|
+
const segments = splitTableCharts(source, streaming);
|
|
898
|
+
segments.forEach((segment, part) => {
|
|
899
|
+
const slot = part === 0 ? `t${index}` : `t${index}.${part}`;
|
|
900
|
+
if (segment.kind === "markdown") {
|
|
901
|
+
const live = streaming && part === segments.length - 1;
|
|
902
|
+
children.push(markdown(slot, slot, linked(segment.text.trim(), live), live));
|
|
903
|
+
return;
|
|
904
|
+
}
|
|
905
|
+
const chart = lookupTableChart(segment.table);
|
|
906
|
+
if (chart instanceof Promise) this.#awaitChart(chart);
|
|
907
|
+
else if (chart) children.push(describeTableChart(chart, slot));
|
|
908
|
+
});
|
|
797
909
|
} else if (content.type === "thinking") {
|
|
798
910
|
const display = resolveThinkingDisplay(content, this.#proseOnlyThinking);
|
|
799
911
|
if (!display.visible) continue;
|
|
@@ -831,8 +943,10 @@ export class AssistantMessageComponent extends Container {
|
|
|
831
943
|
// `.live` while streaming: the body clamps to its tail under a fade.
|
|
832
944
|
role: thinkingLive ? "zeta.thinking.live" : "zeta.thinking",
|
|
833
945
|
collapsible: true,
|
|
834
|
-
// Open while it streams; a finished thought folds to its "Thought for 12s" line.
|
|
835
|
-
collapsed:
|
|
946
|
+
// Open while it streams; a finished thought folds to its "Thought for 12s" line unless the user keeps thinking expanded.
|
|
947
|
+
collapsed:
|
|
948
|
+
this.#thinkingCollapsed.get(index) ??
|
|
949
|
+
(this.#hideThinkingBlock || (!thinkingLive && !this.#expandThinkingBlocks)),
|
|
836
950
|
// Tern's fold head sums it into "Worked for 12s".
|
|
837
951
|
took: clock?.end === undefined ? undefined : Math.max(0, Math.round(clock.end - clock.start)),
|
|
838
952
|
},
|
|
@@ -1074,12 +1188,18 @@ export class AssistantMessageComponent extends Container {
|
|
|
1074
1188
|
for (const child of this.#contentContainer.children) {
|
|
1075
1189
|
const item = items[itemIndex];
|
|
1076
1190
|
if (item?.md === child) {
|
|
1191
|
+
const md = item.md;
|
|
1192
|
+
if (md instanceof FigureMarkdown) {
|
|
1193
|
+
// Plain Markdown reproduces only the prose ahead of the first figure.
|
|
1194
|
+
if (md.leadingProse) parts.push({ kind: item.blockType, text: md.leadingProse });
|
|
1195
|
+
break;
|
|
1196
|
+
}
|
|
1077
1197
|
if (itemIndex === items.length - 1) {
|
|
1078
1198
|
// Streaming child: publish Markdown's frozen prefix, and only
|
|
1079
1199
|
// once non-blank content exists past it — the block's last
|
|
1080
1200
|
// non-blank line is still being written (thinking's prose fold
|
|
1081
1201
|
// may rewrite it) and must stay out of published bytes.
|
|
1082
|
-
const raw =
|
|
1202
|
+
const raw = md.getLastRenderStableText();
|
|
1083
1203
|
const frozen = raw.trim();
|
|
1084
1204
|
if (frozen.length > 0 && /\S/.test(item.lastText.slice(raw.length))) {
|
|
1085
1205
|
parts.push({ kind: item.blockType, text: frozen });
|
|
@@ -1107,7 +1227,7 @@ export class AssistantMessageComponent extends Container {
|
|
|
1107
1227
|
/**
|
|
1108
1228
|
* Render the first `partCount` stable parts, the final one cut to
|
|
1109
1229
|
* `lastLength`. Rows match fresh Markdown renders of each part byte for
|
|
1110
|
-
* byte — {@link #
|
|
1230
|
+
* byte — {@link #createMarkdown} builds the live children too — but a
|
|
1111
1231
|
* closed part renders once per width, and the growing final part reuses one
|
|
1112
1232
|
* Markdown instance so its already-frozen blocks are not re-lexed.
|
|
1113
1233
|
*/
|
|
@@ -1157,7 +1277,7 @@ export class AssistantMessageComponent extends Container {
|
|
|
1157
1277
|
// Trim like the live children, dropping the trailing blank line a frozen
|
|
1158
1278
|
// prefix still carries.
|
|
1159
1279
|
const trimmed = text.trim();
|
|
1160
|
-
if (this.#transcriptBlockFinalized) return this.#
|
|
1280
|
+
if (this.#transcriptBlockFinalized) return this.#createMarkdown(kind, trimmed).render(width);
|
|
1161
1281
|
const head = this.#stableHeadRenderer;
|
|
1162
1282
|
const replay = this.#stableReplayRenderer;
|
|
1163
1283
|
const renderer = role === "head" ? head : role === "replay" ? replay : head?.index === index ? head : replay;
|
|
@@ -1165,29 +1285,74 @@ export class AssistantMessageComponent extends Container {
|
|
|
1165
1285
|
renderer.md.setText(trimmed);
|
|
1166
1286
|
return renderer.md.render(width);
|
|
1167
1287
|
}
|
|
1168
|
-
const md = this.#
|
|
1288
|
+
const md = this.#createMarkdown(kind, trimmed);
|
|
1169
1289
|
if (role === "head") this.#stableHeadRenderer = { index, kind, md };
|
|
1170
1290
|
else if (role === "replay") this.#stableReplayRenderer = { index, kind, md };
|
|
1171
1291
|
return md.render(width);
|
|
1172
1292
|
}
|
|
1173
1293
|
|
|
1174
|
-
/**
|
|
1175
|
-
#
|
|
1294
|
+
/** Markdown for a text or thinking block; live children and stable-row renders share it so stable rows prefix the block render. */
|
|
1295
|
+
#createMarkdown(kind: StablePartKind, text: string): Markdown {
|
|
1176
1296
|
return kind === "text"
|
|
1177
|
-
? new Markdown(
|
|
1178
|
-
text,
|
|
1179
|
-
1,
|
|
1180
|
-
0,
|
|
1181
|
-
this.#getProseTheme(),
|
|
1182
|
-
this.#textColorTransform ? { color: this.#textColorTransform } : undefined,
|
|
1183
|
-
0,
|
|
1184
|
-
)
|
|
1297
|
+
? new Markdown(text, 1, 0, this.#getProseTheme(), this.#getProseTextStyle(), 0)
|
|
1185
1298
|
: new Markdown(text, 1, 0, getMarkdownTheme(), {
|
|
1186
1299
|
color: (value: string) => theme.fg("thinkingText", value),
|
|
1187
1300
|
italic: true,
|
|
1188
1301
|
});
|
|
1189
1302
|
}
|
|
1190
1303
|
|
|
1304
|
+
/**
|
|
1305
|
+
* Whether `text` holds a ```svg fence this terminal draws as a figure, or a
|
|
1306
|
+
* table it draws a chart under. Native (TSP) terminals receive the fence
|
|
1307
|
+
* verbatim in the `md` node and draw it themselves, and get charts as image
|
|
1308
|
+
* nodes from {@link describe}; image-less terminals keep fences as code and
|
|
1309
|
+
* tables as tables.
|
|
1310
|
+
*/
|
|
1311
|
+
#liftsFigures(text: string): boolean {
|
|
1312
|
+
return (
|
|
1313
|
+
this.#showImages &&
|
|
1314
|
+
TERMINAL.imageProtocol !== null &&
|
|
1315
|
+
!isNativeRendering() &&
|
|
1316
|
+
((svgFigureRendering() && hasSvgFence(text)) || (this.#showTableCharts && hasChartTable(text)))
|
|
1317
|
+
);
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
/** Re-describe once a smart table chart pick lands. */
|
|
1321
|
+
#awaitChart(pick: Promise<void>): void {
|
|
1322
|
+
if (this.#chartPicks.has(pick)) return;
|
|
1323
|
+
this.#chartPicks.add(pick);
|
|
1324
|
+
void pick.then(() => {
|
|
1325
|
+
this.#chartPicks.delete(pick);
|
|
1326
|
+
this.#blockVersion++;
|
|
1327
|
+
this.#onImageUpdate?.();
|
|
1328
|
+
});
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1331
|
+
/**
|
|
1332
|
+
* The component for text block `index`: Markdown, or a {@link FigureMarkdown}
|
|
1333
|
+
* when it holds a ```svg fence to lift or a table to chart — taken over from
|
|
1334
|
+
* `previous` so its figures keep their rasters across rebuilds.
|
|
1335
|
+
*/
|
|
1336
|
+
#proseBlock(index: number, text: string, previous: ReadonlyMap<number, FigureMarkdown>): ProseBlock {
|
|
1337
|
+
if (!this.#liftsFigures(text)) return this.#createMarkdown("text", text);
|
|
1338
|
+
let block = previous.get(index);
|
|
1339
|
+
if (block) {
|
|
1340
|
+
block.setText(text);
|
|
1341
|
+
} else {
|
|
1342
|
+
block = new FigureMarkdown(text, {
|
|
1343
|
+
markdown: value => this.#createMarkdown("text", value),
|
|
1344
|
+
charts: this.#showTableCharts,
|
|
1345
|
+
budget: this.#imageBudget,
|
|
1346
|
+
onChange: () => {
|
|
1347
|
+
this.#blockVersion++;
|
|
1348
|
+
this.#onImageUpdate?.();
|
|
1349
|
+
},
|
|
1350
|
+
});
|
|
1351
|
+
}
|
|
1352
|
+
this.#figureBlocks.set(index, block);
|
|
1353
|
+
return block;
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1191
1356
|
#stableLedger(width: number): StableRowLedger {
|
|
1192
1357
|
let ledger = this.#stableLedgers.get(width);
|
|
1193
1358
|
if (ledger === undefined) {
|
|
@@ -1307,6 +1472,18 @@ export class AssistantMessageComponent extends Container {
|
|
|
1307
1472
|
}
|
|
1308
1473
|
}
|
|
1309
1474
|
|
|
1475
|
+
/** Toggle charts under numeric tables (the main session's transcript only, never a subagent's). */
|
|
1476
|
+
setTableChartsVisible(visible: boolean): void {
|
|
1477
|
+
if (this.#showTableCharts === visible) return;
|
|
1478
|
+
this.#showTableCharts = visible;
|
|
1479
|
+
// Figure blocks bake the switch in; rebuild them rather than reuse.
|
|
1480
|
+
for (const block of this.#figureBlocks.values()) block.dispose();
|
|
1481
|
+
this.#figureBlocks.clear();
|
|
1482
|
+
if (this.#lastMessage) {
|
|
1483
|
+
this.updateContent(this.#lastMessage, { transient: this.#lastUpdateTransient });
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1486
|
+
|
|
1310
1487
|
/** Toggle only images produced by tool results; assistant-native images remain governed by setImagesVisible. */
|
|
1311
1488
|
setToolResultImagesVisible(visible: boolean): void {
|
|
1312
1489
|
if (this.#showToolResultImages === visible) return;
|
|
@@ -1330,22 +1507,24 @@ export class AssistantMessageComponent extends Container {
|
|
|
1330
1507
|
}
|
|
1331
1508
|
}
|
|
1332
1509
|
|
|
1510
|
+
/** A displayable Kitty PNG for a non-PNG `image`: the shared cache, else this component's displayed copy. */
|
|
1511
|
+
#kittyConversion(image: ImageContent): ImageContent | undefined {
|
|
1512
|
+
const cached = cachedPngConversion(image);
|
|
1513
|
+
if (cached) return cached;
|
|
1514
|
+
const key = imagePayloadKey(image);
|
|
1515
|
+
return this.#kittyDisplayed.get(key) ?? this.#kittyPreviouslyDisplayed?.get(key);
|
|
1516
|
+
}
|
|
1517
|
+
|
|
1333
1518
|
#convertImagesForKitty(entries: Array<{ image: ImageContent; key: string }>): void {
|
|
1334
1519
|
if (TERMINAL.imageProtocol !== ImageProtocol.Kitty) return;
|
|
1335
1520
|
for (const { image } of entries) {
|
|
1336
|
-
if (image.mimeType === "image/png") continue;
|
|
1521
|
+
if (image.mimeType === "image/png" || this.#kittyConversion(image)) continue;
|
|
1337
1522
|
const key = imagePayloadKey(image);
|
|
1338
|
-
if (this.#kittyConverted.has(key)) continue;
|
|
1339
|
-
const cached = cachedPngConversion(image);
|
|
1340
|
-
if (cached) {
|
|
1341
|
-
this.#kittyConverted.set(key, cached);
|
|
1342
|
-
continue;
|
|
1343
|
-
}
|
|
1344
1523
|
if (this.#kittyConversionsAwaited.has(key)) continue;
|
|
1345
1524
|
this.#kittyConversionsAwaited.add(key);
|
|
1346
1525
|
convertImageToPngShared(image)
|
|
1347
|
-
.then(
|
|
1348
|
-
this.#
|
|
1526
|
+
.then(() => {
|
|
1527
|
+
this.#kittyConversionsAwaited.delete(key);
|
|
1349
1528
|
if (this.#lastMessage) {
|
|
1350
1529
|
this.updateContent(this.#lastMessage, { transient: this.#lastUpdateTransient });
|
|
1351
1530
|
}
|
|
@@ -1363,17 +1542,23 @@ export class AssistantMessageComponent extends Container {
|
|
|
1363
1542
|
|
|
1364
1543
|
if (withLeadingSpacer) this.#contentContainer.addChild(new Spacer(1));
|
|
1365
1544
|
for (const { image, key } of entries) {
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1545
|
+
let displayImage: ImageContent | undefined = image;
|
|
1546
|
+
if (TERMINAL.imageProtocol === ImageProtocol.Kitty && image.mimeType !== "image/png") {
|
|
1547
|
+
displayImage = this.#kittyConversion(image);
|
|
1548
|
+
if (displayImage) this.#kittyDisplayed.set(imagePayloadKey(image), displayImage);
|
|
1549
|
+
}
|
|
1370
1550
|
if (TERMINAL.imageProtocol && displayImage) {
|
|
1371
1551
|
this.#contentContainer.addChild(
|
|
1372
1552
|
new Image(
|
|
1373
1553
|
displayImage.data,
|
|
1374
1554
|
displayImage.mimeType,
|
|
1375
1555
|
{ fallbackColor: (text: string) => theme.fg("toolOutput", text) },
|
|
1376
|
-
{
|
|
1556
|
+
{
|
|
1557
|
+
...resolveImageOptions(),
|
|
1558
|
+
budget: this.#imageBudget,
|
|
1559
|
+
imageKey: key,
|
|
1560
|
+
requestRender: this.#onImageUpdate,
|
|
1561
|
+
},
|
|
1377
1562
|
),
|
|
1378
1563
|
);
|
|
1379
1564
|
continue;
|
|
@@ -1415,10 +1600,14 @@ export class AssistantMessageComponent extends Container {
|
|
|
1415
1600
|
}
|
|
1416
1601
|
|
|
1417
1602
|
#computeShapeKey(message: AssistantMessage): string {
|
|
1418
|
-
const parts: string[] = [
|
|
1603
|
+
const parts: string[] = [
|
|
1604
|
+
`htb:${this.#hideThinkingBlock ? 1 : 0}|pot:${this.#proseOnlyThinking ? 1 : 0}|etb:${this.#expandThinkingBlocks ? 1 : 0}`,
|
|
1605
|
+
];
|
|
1419
1606
|
for (const content of message.content) {
|
|
1420
1607
|
if (content.type === "text") {
|
|
1421
|
-
parts.push(
|
|
1608
|
+
parts.push(
|
|
1609
|
+
!canonicalizeMessage(content.text) ? "T0" : this.#liftsFigures(content.text.trim()) ? "TF" : "T1",
|
|
1610
|
+
);
|
|
1422
1611
|
} else if (content.type === "thinking") {
|
|
1423
1612
|
if (this.#hideThinkingBlock) {
|
|
1424
1613
|
// Match the pulse's empty/nonempty transition without formatting hidden text.
|
|
@@ -1572,6 +1761,8 @@ export class AssistantMessageComponent extends Container {
|
|
|
1572
1761
|
|
|
1573
1762
|
// Clear content container
|
|
1574
1763
|
this.#contentContainer.clear();
|
|
1764
|
+
this.#kittyPreviouslyDisplayed = this.#kittyDisplayed;
|
|
1765
|
+
this.#kittyDisplayed = new Map();
|
|
1575
1766
|
this.#thinkingExtensions.clear();
|
|
1576
1767
|
this.#emergencyText = undefined;
|
|
1577
1768
|
this.#thinkingDots = undefined;
|
|
@@ -1579,9 +1770,9 @@ export class AssistantMessageComponent extends Container {
|
|
|
1579
1770
|
|
|
1580
1771
|
// Determine if we should capture Markdown instances for next fast path
|
|
1581
1772
|
const shouldCapture = this.#canFastPath(message);
|
|
1582
|
-
const captureItems:
|
|
1583
|
-
|
|
1584
|
-
|
|
1773
|
+
const captureItems: FastPathItem[] | undefined = shouldCapture ? [] : undefined;
|
|
1774
|
+
const previousFigures = this.#figureBlocks;
|
|
1775
|
+
this.#figureBlocks = new Map();
|
|
1585
1776
|
|
|
1586
1777
|
const hasVisibleContent = message.content.some(
|
|
1587
1778
|
c =>
|
|
@@ -1600,8 +1791,7 @@ export class AssistantMessageComponent extends Container {
|
|
|
1600
1791
|
if (content.type === "text" && canonicalizeMessage(content.text)) {
|
|
1601
1792
|
// Set paddingY=0 to avoid extra spacing before tool executions
|
|
1602
1793
|
const trimmed = content.text.trim();
|
|
1603
|
-
const
|
|
1604
|
-
const md = new Markdown(trimmed, 1, 0, this.#getProseTheme(), mdOptions, 0);
|
|
1794
|
+
const md = this.#proseBlock(i, trimmed, previousFigures);
|
|
1605
1795
|
this.#contentContainer.addChild(md);
|
|
1606
1796
|
this.#emergencyText = md;
|
|
1607
1797
|
captureItems?.push({ md, contentIndex: i, blockType: "text", lastText: trimmed });
|
|
@@ -1626,10 +1816,7 @@ export class AssistantMessageComponent extends Container {
|
|
|
1626
1816
|
);
|
|
1627
1817
|
|
|
1628
1818
|
// Thinking traces in thinkingText color, italic
|
|
1629
|
-
const md =
|
|
1630
|
-
color: (text: string) => theme.fg("thinkingText", text),
|
|
1631
|
-
italic: true,
|
|
1632
|
-
});
|
|
1819
|
+
const md = this.#createMarkdown("thinking", thinkingText);
|
|
1633
1820
|
md.transientRenderCache = this.#lastUpdateTransient;
|
|
1634
1821
|
this.#contentContainer.addChild(md);
|
|
1635
1822
|
captureItems?.push({ md, contentIndex: i, blockType: "thinking", lastText: thinkingText });
|
|
@@ -1644,6 +1831,9 @@ export class AssistantMessageComponent extends Container {
|
|
|
1644
1831
|
hasRenderedContent ||= this.#showImages;
|
|
1645
1832
|
}
|
|
1646
1833
|
}
|
|
1834
|
+
for (const [index, block] of previousFigures) {
|
|
1835
|
+
if (this.#figureBlocks.get(index) !== block) block.dispose();
|
|
1836
|
+
}
|
|
1647
1837
|
|
|
1648
1838
|
if (this.#shouldAnimateThinking(message)) {
|
|
1649
1839
|
if (hasVisibleContent) this.#contentContainer.addChild(new Spacer(1));
|
|
@@ -1655,6 +1845,7 @@ export class AssistantMessageComponent extends Container {
|
|
|
1655
1845
|
}
|
|
1656
1846
|
|
|
1657
1847
|
this.#renderToolImages();
|
|
1848
|
+
this.#kittyPreviouslyDisplayed = undefined;
|
|
1658
1849
|
const errorPresentation = resolveAssistantErrorPresentation(message);
|
|
1659
1850
|
const hasToolCalls = message.content.some(c => c.type === "toolCall");
|
|
1660
1851
|
if (errorPresentation.kind === "compact-recovered") {
|
|
@@ -25,8 +25,11 @@ export interface ChatTranscriptBuilderDeps {
|
|
|
25
25
|
cwd: string;
|
|
26
26
|
hideThinkingBlock?: () => boolean;
|
|
27
27
|
proseOnlyThinking?: () => boolean;
|
|
28
|
+
expandThinkingBlocks?: () => boolean;
|
|
28
29
|
/** Session-scoped resolved destinations for model-authored Markdown links. */
|
|
29
30
|
linkTargets?: ReadonlyMap<string, string>;
|
|
31
|
+
/** Draw charts under numeric tables; false for transcripts that are not the main session's. Default: true. */
|
|
32
|
+
tableCharts?: boolean;
|
|
30
33
|
requestRender: () => void;
|
|
31
34
|
}
|
|
32
35
|
export declare class ChatTranscriptBuilder {
|