@linxiraos/pi-tui 1.1.27 → 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 +47 -463
- package/README.md +7 -5
- package/dist/types/app-keybindings.d.ts +1 -1
- 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 +2 -2
- 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 +5 -0
- package/dist/types/chat/chat-transcript-builder.d.ts +2 -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 +15 -2
- package/dist/types/chrome/shared.d.ts +9 -0
- 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 +15 -4
- package/dist/types/components/form.d.ts +1 -0
- package/dist/types/components/image.d.ts +12 -44
- 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/markdown.d.ts +36 -3
- 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 +1 -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/backend.d.ts +3 -2
- package/dist/types/native/blobs.d.ts +7 -3
- package/dist/types/native/encode.d.ts +3 -4
- package/dist/types/native/overlay.d.ts +5 -0
- 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 +2 -0
- package/dist/types/overlays/agent-transcript-viewer.d.ts +11 -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/history-search.d.ts +10 -0
- package/dist/types/overlays/hook-selector.d.ts +9 -4
- 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 +23 -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/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/usage-dashboard.d.ts +8 -4
- package/dist/types/overlays/usage-display.d.ts +3 -1
- package/dist/types/prompt/composer.d.ts +1 -1
- package/dist/types/prompt/custom-editor.d.ts +11 -8
- package/dist/types/prompt/image-source.d.ts +2 -1
- package/dist/types/prompt/interactive-context-helpers.d.ts +4 -1
- package/dist/types/prompt/model-mention-autocomplete.d.ts +1 -1
- package/dist/types/prompt/welcome.d.ts +6 -1
- 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/status-line/component.d.ts +3 -0
- 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 +27 -35
- package/dist/types/terminal-multiplexer.d.ts +127 -14
- package/dist/types/terminal.d.ts +22 -6
- 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/tmux.d.ts +11 -4
- 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 +0 -4
- package/dist/types/tools/write.d.ts +9 -2
- package/dist/types/tools/yield.d.ts +22 -0
- package/dist/types/tui.d.ts +31 -0
- package/dist/types/utils.d.ts +6 -0
- package/dist/types/vim.d.ts +19 -5
- package/package.json +13 -9
- package/src/app-keybindings.d.ts +1 -1
- package/src/app-keybindings.ts +1 -1
- 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 +26 -5
- package/src/apps/git/git-tui.ts +2 -2
- package/src/apps/git/help.ts +29 -20
- 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/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 +5 -0
- package/src/chat/assistant-message.ts +219 -50
- package/src/chat/chat-transcript-builder.d.ts +2 -0
- package/src/chat/chat-transcript-builder.ts +5 -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 +80 -6
- package/src/chat/user-message.d.ts +15 -2
- package/src/chat/user-message.ts +55 -3
- package/src/chrome/shared.ts +53 -0
- package/src/chrome/transcript-container.d.ts +6 -0
- package/src/chrome/transcript-container.ts +93 -13
- 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 +15 -4
- package/src/components/editor.ts +294 -46
- package/src/components/form.d.ts +1 -0
- package/src/components/form.ts +8 -0
- package/src/components/image.d.ts +12 -44
- package/src/components/image.ts +47 -151
- 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/markdown.d.ts +36 -3
- package/src/components/markdown.ts +433 -318
- 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 +1 -1
- package/src/fuzzy.d.ts +29 -1
- package/src/fuzzy.ts +115 -30
- package/src/key-hint-format.d.ts +1 -1
- package/src/key-hint-format.ts +1 -1
- 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 +4 -4
- 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/backend.d.ts +3 -2
- package/src/native/backend.ts +174 -18
- package/src/native/blobs.d.ts +7 -3
- package/src/native/blobs.ts +47 -12
- package/src/native/encode.d.ts +3 -4
- package/src/native/encode.ts +58 -31
- package/src/native/overlay.d.ts +5 -0
- package/src/native/overlay.ts +13 -0
- package/src/native/reconcile.ts +4 -0
- 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 +44 -29
- package/src/overlays/agent-transcript-viewer.ts +91 -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 +7 -10
- package/src/overlays/btw-history-panel.ts +12 -1
- package/src/overlays/copy-selector.ts +30 -8
- 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/jobs-panel.ts +28 -46
- package/src/overlays/model-browser.ts +341 -110
- package/src/overlays/model-hub.ts +300 -49
- package/src/overlays/move-overlay.ts +28 -14
- package/src/overlays/oauth-selector.ts +54 -70
- package/src/overlays/plan-review-overlay.ts +194 -70
- package/src/overlays/plugin-settings.ts +3 -3
- package/src/overlays/report-panel.ts +2 -2
- package/src/overlays/rewind-selector.ts +86 -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 +4 -3
- 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/tree-selector.ts +41 -20
- package/src/overlays/usage-dashboard.ts +100 -50
- package/src/overlays/usage-display.ts +10 -4
- package/src/prompt/attachment-chips.ts +15 -11
- package/src/prompt/composer-attachments.ts +6 -4
- package/src/prompt/composer.ts +4 -15
- package/src/prompt/custom-editor.ts +54 -26
- package/src/prompt/image-source.ts +2 -1
- package/src/prompt/interactive-context-helpers.ts +30 -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/tips.txt +6 -5
- package/src/prompt/welcome.ts +55 -12
- 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 +11 -0
- 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/status-line/component.ts +292 -78
- 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 +27 -35
- package/src/terminal-capabilities.ts +75 -189
- package/src/terminal-multiplexer.d.ts +127 -14
- package/src/terminal-multiplexer.ts +112 -33
- package/src/terminal.d.ts +22 -6
- package/src/terminal.ts +139 -37
- 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 +6 -1
- 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/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.ts +92 -42
- 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 +149 -50
- package/src/tools/task.d.ts +0 -4
- package/src/tools/task.ts +112 -59
- 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 +31 -0
- package/src/tui.ts +309 -160
- 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.ts +8 -0
|
@@ -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
|
},
|
|
@@ -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
|
|
@@ -86,6 +87,8 @@ export declare class AssistantMessageComponent extends Container {
|
|
|
86
87
|
*/
|
|
87
88
|
setExpanded(expanded: boolean): void;
|
|
88
89
|
isTranscriptBlockFinalized(): boolean;
|
|
90
|
+
/** Whether a figure still waits for the raster of its final source; the transcript holds retirement meanwhile. */
|
|
91
|
+
isTranscriptBlockPending(): boolean;
|
|
89
92
|
render(width: number): readonly string[];
|
|
90
93
|
/**
|
|
91
94
|
* A `col` (role `zeta.assistant`) of `md` nodes keyed by content index, so
|
|
@@ -123,6 +126,8 @@ export declare class AssistantMessageComponent extends Container {
|
|
|
123
126
|
messagePersistenceKey(): string | undefined;
|
|
124
127
|
/** Toggle rendering for assistant-native and tool-result images. */
|
|
125
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;
|
|
126
131
|
/** Toggle only images produced by tool results; assistant-native images remain governed by setImagesVisible. */
|
|
127
132
|
setToolResultImagesVisible(visible: boolean): void;
|
|
128
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
|
}
|
|
@@ -537,11 +594,18 @@ export class AssistantMessageComponent extends Container {
|
|
|
537
594
|
this.#markdownTheme = undefined;
|
|
538
595
|
this.#fastPathKey = undefined;
|
|
539
596
|
this.#fastPathItems = undefined;
|
|
597
|
+
for (const block of this.#figureBlocks.values()) block.restyle();
|
|
540
598
|
if (this.#lastMessage) {
|
|
541
599
|
this.updateContent(this.#lastMessage, { transient: this.#lastUpdateTransient });
|
|
542
600
|
}
|
|
543
601
|
}
|
|
544
602
|
|
|
603
|
+
override releaseRenderCaches(): void {
|
|
604
|
+
super.releaseRenderCaches();
|
|
605
|
+
this.#dropStableRenders();
|
|
606
|
+
this.#emergencyText?.releaseRenderCaches();
|
|
607
|
+
}
|
|
608
|
+
|
|
545
609
|
setHideThinkingBlock(hide: boolean): void {
|
|
546
610
|
this.#hideThinkingBlock = hide;
|
|
547
611
|
this.#thinkingCollapsed.clear();
|
|
@@ -713,6 +777,12 @@ export class AssistantMessageComponent extends Container {
|
|
|
713
777
|
return this.#transcriptBlockFinalized;
|
|
714
778
|
}
|
|
715
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
|
+
|
|
716
786
|
override render(width: number): readonly string[] {
|
|
717
787
|
const rows = super.render(width);
|
|
718
788
|
this.#publishStableSnapshot(rows, width);
|
|
@@ -806,7 +876,36 @@ export class AssistantMessageComponent extends Container {
|
|
|
806
876
|
const content = message.content[index]!;
|
|
807
877
|
const streaming = live && index === tailIndex;
|
|
808
878
|
if (content.type === "text" && canonicalizeMessage(content.text)) {
|
|
809
|
-
|
|
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
|
+
});
|
|
810
909
|
} else if (content.type === "thinking") {
|
|
811
910
|
const display = resolveThinkingDisplay(content, this.#proseOnlyThinking);
|
|
812
911
|
if (!display.visible) continue;
|
|
@@ -1089,12 +1188,18 @@ export class AssistantMessageComponent extends Container {
|
|
|
1089
1188
|
for (const child of this.#contentContainer.children) {
|
|
1090
1189
|
const item = items[itemIndex];
|
|
1091
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
|
+
}
|
|
1092
1197
|
if (itemIndex === items.length - 1) {
|
|
1093
1198
|
// Streaming child: publish Markdown's frozen prefix, and only
|
|
1094
1199
|
// once non-blank content exists past it — the block's last
|
|
1095
1200
|
// non-blank line is still being written (thinking's prose fold
|
|
1096
1201
|
// may rewrite it) and must stay out of published bytes.
|
|
1097
|
-
const raw =
|
|
1202
|
+
const raw = md.getLastRenderStableText();
|
|
1098
1203
|
const frozen = raw.trim();
|
|
1099
1204
|
if (frozen.length > 0 && /\S/.test(item.lastText.slice(raw.length))) {
|
|
1100
1205
|
parts.push({ kind: item.blockType, text: frozen });
|
|
@@ -1122,7 +1227,7 @@ export class AssistantMessageComponent extends Container {
|
|
|
1122
1227
|
/**
|
|
1123
1228
|
* Render the first `partCount` stable parts, the final one cut to
|
|
1124
1229
|
* `lastLength`. Rows match fresh Markdown renders of each part byte for
|
|
1125
|
-
* byte — {@link #
|
|
1230
|
+
* byte — {@link #createMarkdown} builds the live children too — but a
|
|
1126
1231
|
* closed part renders once per width, and the growing final part reuses one
|
|
1127
1232
|
* Markdown instance so its already-frozen blocks are not re-lexed.
|
|
1128
1233
|
*/
|
|
@@ -1172,7 +1277,7 @@ export class AssistantMessageComponent extends Container {
|
|
|
1172
1277
|
// Trim like the live children, dropping the trailing blank line a frozen
|
|
1173
1278
|
// prefix still carries.
|
|
1174
1279
|
const trimmed = text.trim();
|
|
1175
|
-
if (this.#transcriptBlockFinalized) return this.#
|
|
1280
|
+
if (this.#transcriptBlockFinalized) return this.#createMarkdown(kind, trimmed).render(width);
|
|
1176
1281
|
const head = this.#stableHeadRenderer;
|
|
1177
1282
|
const replay = this.#stableReplayRenderer;
|
|
1178
1283
|
const renderer = role === "head" ? head : role === "replay" ? replay : head?.index === index ? head : replay;
|
|
@@ -1180,29 +1285,74 @@ export class AssistantMessageComponent extends Container {
|
|
|
1180
1285
|
renderer.md.setText(trimmed);
|
|
1181
1286
|
return renderer.md.render(width);
|
|
1182
1287
|
}
|
|
1183
|
-
const md = this.#
|
|
1288
|
+
const md = this.#createMarkdown(kind, trimmed);
|
|
1184
1289
|
if (role === "head") this.#stableHeadRenderer = { index, kind, md };
|
|
1185
1290
|
else if (role === "replay") this.#stableReplayRenderer = { index, kind, md };
|
|
1186
1291
|
return md.render(width);
|
|
1187
1292
|
}
|
|
1188
1293
|
|
|
1189
|
-
/**
|
|
1190
|
-
#
|
|
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 {
|
|
1191
1296
|
return kind === "text"
|
|
1192
|
-
? new Markdown(
|
|
1193
|
-
text,
|
|
1194
|
-
1,
|
|
1195
|
-
0,
|
|
1196
|
-
this.#getProseTheme(),
|
|
1197
|
-
this.#textColorTransform ? { color: this.#textColorTransform } : undefined,
|
|
1198
|
-
0,
|
|
1199
|
-
)
|
|
1297
|
+
? new Markdown(text, 1, 0, this.#getProseTheme(), this.#getProseTextStyle(), 0)
|
|
1200
1298
|
: new Markdown(text, 1, 0, getMarkdownTheme(), {
|
|
1201
1299
|
color: (value: string) => theme.fg("thinkingText", value),
|
|
1202
1300
|
italic: true,
|
|
1203
1301
|
});
|
|
1204
1302
|
}
|
|
1205
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
|
+
|
|
1206
1356
|
#stableLedger(width: number): StableRowLedger {
|
|
1207
1357
|
let ledger = this.#stableLedgers.get(width);
|
|
1208
1358
|
if (ledger === undefined) {
|
|
@@ -1322,6 +1472,18 @@ export class AssistantMessageComponent extends Container {
|
|
|
1322
1472
|
}
|
|
1323
1473
|
}
|
|
1324
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
|
+
|
|
1325
1487
|
/** Toggle only images produced by tool results; assistant-native images remain governed by setImagesVisible. */
|
|
1326
1488
|
setToolResultImagesVisible(visible: boolean): void {
|
|
1327
1489
|
if (this.#showToolResultImages === visible) return;
|
|
@@ -1345,22 +1507,24 @@ export class AssistantMessageComponent extends Container {
|
|
|
1345
1507
|
}
|
|
1346
1508
|
}
|
|
1347
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
|
+
|
|
1348
1518
|
#convertImagesForKitty(entries: Array<{ image: ImageContent; key: string }>): void {
|
|
1349
1519
|
if (TERMINAL.imageProtocol !== ImageProtocol.Kitty) return;
|
|
1350
1520
|
for (const { image } of entries) {
|
|
1351
|
-
if (image.mimeType === "image/png") continue;
|
|
1521
|
+
if (image.mimeType === "image/png" || this.#kittyConversion(image)) continue;
|
|
1352
1522
|
const key = imagePayloadKey(image);
|
|
1353
|
-
if (this.#kittyConverted.has(key)) continue;
|
|
1354
|
-
const cached = cachedPngConversion(image);
|
|
1355
|
-
if (cached) {
|
|
1356
|
-
this.#kittyConverted.set(key, cached);
|
|
1357
|
-
continue;
|
|
1358
|
-
}
|
|
1359
1523
|
if (this.#kittyConversionsAwaited.has(key)) continue;
|
|
1360
1524
|
this.#kittyConversionsAwaited.add(key);
|
|
1361
1525
|
convertImageToPngShared(image)
|
|
1362
|
-
.then(
|
|
1363
|
-
this.#
|
|
1526
|
+
.then(() => {
|
|
1527
|
+
this.#kittyConversionsAwaited.delete(key);
|
|
1364
1528
|
if (this.#lastMessage) {
|
|
1365
1529
|
this.updateContent(this.#lastMessage, { transient: this.#lastUpdateTransient });
|
|
1366
1530
|
}
|
|
@@ -1378,10 +1542,11 @@ export class AssistantMessageComponent extends Container {
|
|
|
1378
1542
|
|
|
1379
1543
|
if (withLeadingSpacer) this.#contentContainer.addChild(new Spacer(1));
|
|
1380
1544
|
for (const { image, key } of entries) {
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
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
|
+
}
|
|
1385
1550
|
if (TERMINAL.imageProtocol && displayImage) {
|
|
1386
1551
|
this.#contentContainer.addChild(
|
|
1387
1552
|
new Image(
|
|
@@ -1440,7 +1605,9 @@ export class AssistantMessageComponent extends Container {
|
|
|
1440
1605
|
];
|
|
1441
1606
|
for (const content of message.content) {
|
|
1442
1607
|
if (content.type === "text") {
|
|
1443
|
-
parts.push(
|
|
1608
|
+
parts.push(
|
|
1609
|
+
!canonicalizeMessage(content.text) ? "T0" : this.#liftsFigures(content.text.trim()) ? "TF" : "T1",
|
|
1610
|
+
);
|
|
1444
1611
|
} else if (content.type === "thinking") {
|
|
1445
1612
|
if (this.#hideThinkingBlock) {
|
|
1446
1613
|
// Match the pulse's empty/nonempty transition without formatting hidden text.
|
|
@@ -1594,6 +1761,8 @@ export class AssistantMessageComponent extends Container {
|
|
|
1594
1761
|
|
|
1595
1762
|
// Clear content container
|
|
1596
1763
|
this.#contentContainer.clear();
|
|
1764
|
+
this.#kittyPreviouslyDisplayed = this.#kittyDisplayed;
|
|
1765
|
+
this.#kittyDisplayed = new Map();
|
|
1597
1766
|
this.#thinkingExtensions.clear();
|
|
1598
1767
|
this.#emergencyText = undefined;
|
|
1599
1768
|
this.#thinkingDots = undefined;
|
|
@@ -1601,9 +1770,9 @@ export class AssistantMessageComponent extends Container {
|
|
|
1601
1770
|
|
|
1602
1771
|
// Determine if we should capture Markdown instances for next fast path
|
|
1603
1772
|
const shouldCapture = this.#canFastPath(message);
|
|
1604
|
-
const captureItems:
|
|
1605
|
-
|
|
1606
|
-
|
|
1773
|
+
const captureItems: FastPathItem[] | undefined = shouldCapture ? [] : undefined;
|
|
1774
|
+
const previousFigures = this.#figureBlocks;
|
|
1775
|
+
this.#figureBlocks = new Map();
|
|
1607
1776
|
|
|
1608
1777
|
const hasVisibleContent = message.content.some(
|
|
1609
1778
|
c =>
|
|
@@ -1622,8 +1791,7 @@ export class AssistantMessageComponent extends Container {
|
|
|
1622
1791
|
if (content.type === "text" && canonicalizeMessage(content.text)) {
|
|
1623
1792
|
// Set paddingY=0 to avoid extra spacing before tool executions
|
|
1624
1793
|
const trimmed = content.text.trim();
|
|
1625
|
-
const
|
|
1626
|
-
const md = new Markdown(trimmed, 1, 0, this.#getProseTheme(), mdOptions, 0);
|
|
1794
|
+
const md = this.#proseBlock(i, trimmed, previousFigures);
|
|
1627
1795
|
this.#contentContainer.addChild(md);
|
|
1628
1796
|
this.#emergencyText = md;
|
|
1629
1797
|
captureItems?.push({ md, contentIndex: i, blockType: "text", lastText: trimmed });
|
|
@@ -1648,10 +1816,7 @@ export class AssistantMessageComponent extends Container {
|
|
|
1648
1816
|
);
|
|
1649
1817
|
|
|
1650
1818
|
// Thinking traces in thinkingText color, italic
|
|
1651
|
-
const md =
|
|
1652
|
-
color: (text: string) => theme.fg("thinkingText", text),
|
|
1653
|
-
italic: true,
|
|
1654
|
-
});
|
|
1819
|
+
const md = this.#createMarkdown("thinking", thinkingText);
|
|
1655
1820
|
md.transientRenderCache = this.#lastUpdateTransient;
|
|
1656
1821
|
this.#contentContainer.addChild(md);
|
|
1657
1822
|
captureItems?.push({ md, contentIndex: i, blockType: "thinking", lastText: thinkingText });
|
|
@@ -1666,6 +1831,9 @@ export class AssistantMessageComponent extends Container {
|
|
|
1666
1831
|
hasRenderedContent ||= this.#showImages;
|
|
1667
1832
|
}
|
|
1668
1833
|
}
|
|
1834
|
+
for (const [index, block] of previousFigures) {
|
|
1835
|
+
if (this.#figureBlocks.get(index) !== block) block.dispose();
|
|
1836
|
+
}
|
|
1669
1837
|
|
|
1670
1838
|
if (this.#shouldAnimateThinking(message)) {
|
|
1671
1839
|
if (hasVisibleContent) this.#contentContainer.addChild(new Spacer(1));
|
|
@@ -1677,6 +1845,7 @@ export class AssistantMessageComponent extends Container {
|
|
|
1677
1845
|
}
|
|
1678
1846
|
|
|
1679
1847
|
this.#renderToolImages();
|
|
1848
|
+
this.#kittyPreviouslyDisplayed = undefined;
|
|
1680
1849
|
const errorPresentation = resolveAssistantErrorPresentation(message);
|
|
1681
1850
|
const hasToolCalls = message.content.some(c => c.type === "toolCall");
|
|
1682
1851
|
if (errorPresentation.kind === "compact-recovered") {
|
|
@@ -28,6 +28,8 @@ export interface ChatTranscriptBuilderDeps {
|
|
|
28
28
|
expandThinkingBlocks?: () => boolean;
|
|
29
29
|
/** Session-scoped resolved destinations for model-authored Markdown links. */
|
|
30
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;
|
|
31
33
|
requestRender: () => void;
|
|
32
34
|
}
|
|
33
35
|
export declare class ChatTranscriptBuilder {
|
|
@@ -83,6 +83,8 @@ export interface ChatTranscriptBuilderDeps {
|
|
|
83
83
|
expandThinkingBlocks?: () => boolean;
|
|
84
84
|
/** Session-scoped resolved destinations for model-authored Markdown links. */
|
|
85
85
|
linkTargets?: ReadonlyMap<string, string>;
|
|
86
|
+
/** Draw charts under numeric tables; false for transcripts that are not the main session's. Default: true. */
|
|
87
|
+
tableCharts?: boolean;
|
|
86
88
|
requestRender: () => void;
|
|
87
89
|
}
|
|
88
90
|
|
|
@@ -221,9 +223,7 @@ export class ChatTranscriptBuilder {
|
|
|
221
223
|
}
|
|
222
224
|
if (previous.canBeDisplacedBy(nextToolName)) {
|
|
223
225
|
this.#todoSnapshot = null;
|
|
224
|
-
|
|
225
|
-
this.container.removeChild(previous);
|
|
226
|
-
}
|
|
226
|
+
this.container.removeChild(previous);
|
|
227
227
|
previous.seal();
|
|
228
228
|
return;
|
|
229
229
|
}
|
|
@@ -419,6 +419,7 @@ export class ChatTranscriptBuilder {
|
|
|
419
419
|
);
|
|
420
420
|
assistantComponent.setImagesVisible(displayPreferences.showImages);
|
|
421
421
|
assistantComponent.setToolResultImagesVisible(!displayPreferences.hideToolActivity);
|
|
422
|
+
assistantComponent.setTableChartsVisible(this.#deps.tableCharts !== false);
|
|
422
423
|
this.#trackExpandable(assistantComponent);
|
|
423
424
|
assistantComponent.pickReactionTarget(this.container.children);
|
|
424
425
|
this.container.addChild(assistantComponent);
|
|
@@ -457,6 +458,7 @@ export class ChatTranscriptBuilder {
|
|
|
457
458
|
);
|
|
458
459
|
component.setImagesVisible(displayPreferences.showImages);
|
|
459
460
|
component.setToolResultImagesVisible(!displayPreferences.hideToolActivity);
|
|
461
|
+
component.setTableChartsVisible(this.#deps.tableCharts !== false);
|
|
460
462
|
this.#trackExpandable(component);
|
|
461
463
|
this.container.addChild(component);
|
|
462
464
|
lastAssistantComponent = component;
|
|
@@ -31,6 +31,7 @@ declare class SummaryMessageComponent implements Component {
|
|
|
31
31
|
handleNativeEvent(event: NativeUiEvent): void;
|
|
32
32
|
setIgnoreTight(ignore: boolean): this;
|
|
33
33
|
invalidate(): void;
|
|
34
|
+
releaseRenderCaches(): void;
|
|
34
35
|
dispose(): void;
|
|
35
36
|
render(width: number): readonly string[];
|
|
36
37
|
}
|
|
@@ -53,6 +53,10 @@ class DividerSummary implements Component {
|
|
|
53
53
|
this.#cache = undefined;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
releaseRenderCaches(): void {
|
|
57
|
+
this.#cache = undefined;
|
|
58
|
+
}
|
|
59
|
+
|
|
56
60
|
render(width: number): readonly string[] {
|
|
57
61
|
width = Math.max(1, width);
|
|
58
62
|
if (this.#cache?.width === width) return this.#cache.lines;
|
|
@@ -150,6 +154,10 @@ class SummaryMessageComponent implements Component {
|
|
|
150
154
|
if (this.#ignoreTight !== undefined) this.#disclosure.setIgnoreTight(this.#ignoreTight);
|
|
151
155
|
}
|
|
152
156
|
|
|
157
|
+
releaseRenderCaches(): void {
|
|
158
|
+
this.#disclosure.releaseRenderCaches();
|
|
159
|
+
}
|
|
160
|
+
|
|
153
161
|
dispose(): void {
|
|
154
162
|
if (this.#disposed) return;
|
|
155
163
|
this.#disposed = true;
|
|
@@ -78,7 +78,7 @@ export interface ExecutionToolInput {
|
|
|
78
78
|
/** Tool name for the head icon (`bash`, `eval`). */
|
|
79
79
|
readonly name: string;
|
|
80
80
|
readonly title: string;
|
|
81
|
-
/** The command / cell source
|
|
81
|
+
/** The command / cell source: the run box's input and the head's Copy command. */
|
|
82
82
|
readonly command: string;
|
|
83
83
|
readonly lang: string;
|
|
84
84
|
/** Excluded from context (`!!`/`$$`). */
|
|
@@ -96,10 +96,11 @@ export interface ExecutionToolInput {
|
|
|
96
96
|
readonly artifactError?: OutputArtifactError;
|
|
97
97
|
}
|
|
98
98
|
/**
|
|
99
|
-
* A user `!`/`$` run as the agent's `tool` frame (§6.6):
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
99
|
+
* A user `!`/`$` run as the agent's `tool` frame (§6.6): a head with a `you`
|
|
100
|
+
* badge (plus `not sent` when excluded from context) and the command for Copy
|
|
101
|
+
* command; the body is one run box — the source, the output as an `ansi` mini
|
|
102
|
+
* terminal following its tail, and the foot (state, time, truncation) — with
|
|
103
|
+
* any images below it. Timer, exit chip and cancelled note stay head data.
|
|
103
104
|
*/
|
|
104
105
|
export declare function describeExecutionTool(input: ExecutionToolInput): NativeNode;
|
|
105
106
|
/**
|