@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
package/src/tools/write.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import * as path from "node:path";
|
|
1
2
|
import type { HighlightStream } from "@linxiraos/pi-natives";
|
|
2
3
|
import type { Component } from "../tui";
|
|
4
|
+
import { fencedCode } from "../components/markdown";
|
|
3
5
|
import { Text } from "../components/text";
|
|
4
6
|
import { getLanguageFromPath } from "../lang-from-path";
|
|
5
7
|
import { createHighlightStream, highlightCode, type Theme } from "../theme/theme";
|
|
@@ -31,8 +33,7 @@ import {
|
|
|
31
33
|
type ProcWriteDetails,
|
|
32
34
|
} from "./proc-render";
|
|
33
35
|
import type { TspTone } from "@linxiraos/pi-wire";
|
|
34
|
-
import { code, compact, node, span } from "../native/describe";
|
|
35
|
-
import { registerNativeBlob } from "../native/blobs";
|
|
36
|
+
import { code, compact, md, node, span } from "../native/describe";
|
|
36
37
|
import type { NativeChild, NativeNode } from "../native/node";
|
|
37
38
|
import { diagnosticsBadge, diagnosticsSection, displayPath, errorText, fileHref, resultText } from "./native-view";
|
|
38
39
|
import { describeCfgWrite, renderCfgWrite, type CfgWriteDetails } from "./cfg-render";
|
|
@@ -45,6 +46,7 @@ import type {
|
|
|
45
46
|
RenderResultOptions,
|
|
46
47
|
ToolActivityContext,
|
|
47
48
|
ToolActivitySummary,
|
|
49
|
+
ToolFigure,
|
|
48
50
|
ToolRenderer,
|
|
49
51
|
} from "./renderer";
|
|
50
52
|
import { splitUrlScheme } from "./url-scheme-host";
|
|
@@ -91,8 +93,31 @@ interface WriteRenderArgs {
|
|
|
91
93
|
const WRITE_PREVIEW_LINES = 6;
|
|
92
94
|
/** Collapsed native write body: the first lines of the file (§7.3). */
|
|
93
95
|
const NATIVE_WRITE_PREVIEW = { lines: 8 } as const;
|
|
94
|
-
/**
|
|
95
|
-
|
|
96
|
+
/**
|
|
97
|
+
* Collapsed clamp of a write drawn as a figure: Tern's model figure (its head
|
|
98
|
+
* over a 320px stage) clears the clamp's fade at 16px lines.
|
|
99
|
+
*/
|
|
100
|
+
const NATIVE_FIGURE_PREVIEW_LINES = 26;
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Fence language per extension of a written file that transcripts draw as a
|
|
104
|
+
* figure, as they draw that fence in assistant text: svg and mermaid, plus
|
|
105
|
+
* the 3D formats only Tern draws (stencil-markdown's model fences).
|
|
106
|
+
*/
|
|
107
|
+
const FIGURE_FENCES: Readonly<Record<string, string>> = {
|
|
108
|
+
svg: "svg",
|
|
109
|
+
mmd: "mermaid",
|
|
110
|
+
mermaid: "mermaid",
|
|
111
|
+
obj: "obj",
|
|
112
|
+
ply: "ply",
|
|
113
|
+
wrl: "wrl",
|
|
114
|
+
vrml: "vrml",
|
|
115
|
+
x3dv: "x3dv",
|
|
116
|
+
stl: "stl",
|
|
117
|
+
gltf: "gltf",
|
|
118
|
+
usda: "usda",
|
|
119
|
+
usd: "usd",
|
|
120
|
+
};
|
|
96
121
|
|
|
97
122
|
/** A write tool result as the renderer receives it. */
|
|
98
123
|
interface WriteResult {
|
|
@@ -101,26 +126,46 @@ interface WriteResult {
|
|
|
101
126
|
isError?: boolean;
|
|
102
127
|
}
|
|
103
128
|
|
|
104
|
-
/**
|
|
105
|
-
|
|
129
|
+
/**
|
|
130
|
+
* The fence a file write draws as while its content streams and after: its
|
|
131
|
+
* extension names a figure language and it has content, closed once the args
|
|
132
|
+
* are final. None for URL-card writes, or after an error.
|
|
133
|
+
*/
|
|
134
|
+
function writeFigure(
|
|
135
|
+
args: WriteRenderArgs | undefined,
|
|
136
|
+
result: WriteResult | undefined,
|
|
137
|
+
options: RenderResultOptions,
|
|
138
|
+
): ToolFigure | undefined {
|
|
139
|
+
if (result?.isError || result?.details?.xdev) return undefined;
|
|
140
|
+
const rawPath =
|
|
141
|
+
typeof args?.file_path === "string" ? args.file_path : typeof args?.path === "string" ? args.path : "";
|
|
142
|
+
const lang = FIGURE_FENCES[path.extname(rawPath).slice(1).toLowerCase()];
|
|
143
|
+
const content = args?.content;
|
|
144
|
+
if (!lang || typeof content !== "string" || !/\S/.test(content) || writeUrlCard(rawPath, result?.details)) {
|
|
145
|
+
return undefined;
|
|
146
|
+
}
|
|
147
|
+
return {
|
|
148
|
+
lang,
|
|
149
|
+
source: content.includes("\r") ? content.replace(/\r/g, "") : content,
|
|
150
|
+
closed: result !== undefined || options.argsComplete === true,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
106
153
|
|
|
107
|
-
|
|
108
|
-
|
|
154
|
+
/**
|
|
155
|
+
* The `md` node drawing a figure write as its fence draws in a reply: open
|
|
156
|
+
* and streaming while the content arrives. A mermaid figure waits for the
|
|
157
|
+
* fence to close, as Tern shows an open one as code, which the source is.
|
|
158
|
+
*/
|
|
159
|
+
function describeFigure(figure: ToolFigure | undefined): NativeNode | undefined {
|
|
160
|
+
if (!figure || (figure.lang === "mermaid" && !figure.closed)) return undefined;
|
|
161
|
+
const text = fencedCode(figure.lang, figure.source, { open: !figure.closed });
|
|
162
|
+
return { ...md(text, { role: "zeta.tool.write.figure", stream: !figure.closed }), key: "figure" };
|
|
109
163
|
}
|
|
110
164
|
|
|
111
|
-
/**
|
|
112
|
-
function
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
const blob = registerNativeBlob(new TextEncoder().encode(content), "image/svg+xml");
|
|
116
|
-
const preview = node("image", {
|
|
117
|
-
blob,
|
|
118
|
-
alt,
|
|
119
|
-
role: "zeta.tool.write.image",
|
|
120
|
-
max: { h: `${NATIVE_SVG_PREVIEW_LINES}lines` },
|
|
121
|
-
});
|
|
122
|
-
result[kSvgPreview] = { content, node: preview };
|
|
123
|
-
return preview;
|
|
165
|
+
/** The written content as numbered code, keyed so it keeps its node as the figure above it comes and goes. */
|
|
166
|
+
function describeSource(rawPath: string, content: string): NativeNode {
|
|
167
|
+
const lang = rawPath ? getLanguageFromPath(rawPath) : undefined;
|
|
168
|
+
return { ...code(content, { lang, numbers: true }), key: "source" };
|
|
124
169
|
}
|
|
125
170
|
|
|
126
171
|
function countLines(text: string): number {
|
|
@@ -592,40 +637,49 @@ export const writeToolRenderer = {
|
|
|
592
637
|
// back to the normalizing stringify.
|
|
593
638
|
const content = typeof args.content === "string" ? args.content : normalizeDisplayText(args.content);
|
|
594
639
|
const streamingCache = createRenderedStringCache();
|
|
595
|
-
return framedToolCard(
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
640
|
+
return framedToolCard(
|
|
641
|
+
uiTheme,
|
|
642
|
+
() => {
|
|
643
|
+
const body = content
|
|
644
|
+
? formatStreamingContent(
|
|
645
|
+
content,
|
|
646
|
+
Boolean(options?.expanded),
|
|
647
|
+
lang,
|
|
648
|
+
uiTheme,
|
|
649
|
+
options?.spinnerFrame,
|
|
650
|
+
streamingCache,
|
|
651
|
+
// `options` is the ToolExecutionComponent's persistent
|
|
652
|
+
// render-state object — a stable identity across reveal ticks
|
|
653
|
+
// that keys the incremental preview state. `argsComplete`
|
|
654
|
+
// flushes the trailing line through the highlighter once.
|
|
655
|
+
options,
|
|
656
|
+
options?.argsComplete,
|
|
657
|
+
)
|
|
658
|
+
: "";
|
|
659
|
+
const bodyLines = body ? body.split("\n") : [];
|
|
660
|
+
while (bodyLines.length > 0 && bodyLines[0].trim() === "") bodyLines.shift();
|
|
661
|
+
return {
|
|
662
|
+
header,
|
|
663
|
+
sections: bodyLines.length > 0 ? [{ content: bodyLines }] : [],
|
|
664
|
+
phase: "pending",
|
|
665
|
+
borderColor: "borderMuted",
|
|
666
|
+
};
|
|
667
|
+
},
|
|
668
|
+
// The highlighted body in `streamingCache` does not depend on width, and re-highlighting every committed
|
|
669
|
+
// card on each resize replay costs seconds; only the incremental highlighter state goes.
|
|
670
|
+
{ onReleaseRenderCaches: () => delete options?.[writeStreamingPreviewStateKey] },
|
|
671
|
+
);
|
|
621
672
|
},
|
|
622
673
|
|
|
623
674
|
renderResult(
|
|
624
675
|
result: WriteResult,
|
|
625
|
-
options: RenderResultOptions & { renderContext?: WriteRenderContext },
|
|
676
|
+
options: RenderResultOptions & WriteStreamingPreviewStateCarrier & { renderContext?: WriteRenderContext },
|
|
626
677
|
uiTheme: Theme,
|
|
627
678
|
args?: WriteRenderArgs,
|
|
628
679
|
): Component {
|
|
680
|
+
// Write merges call and result, so this builder runs once per display rebuild after the result arrives and the
|
|
681
|
+
// call card never renders again: its incremental highlighter state is dead from here on.
|
|
682
|
+
delete options[writeStreamingPreviewStateKey];
|
|
629
683
|
const cardPath =
|
|
630
684
|
typeof args?.path === "string" ? args.path : typeof args?.file_path === "string" ? args.file_path : "";
|
|
631
685
|
const routed = writeUrlCard(cardPath, result.details);
|
|
@@ -687,36 +741,41 @@ export const writeToolRenderer = {
|
|
|
687
741
|
const diagnostics = result.details?.diagnostics;
|
|
688
742
|
|
|
689
743
|
const previewCache = createRenderedStringCache();
|
|
690
|
-
return framedToolCard(
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
const diagText = formatDiagnostics(diagnostics, expanded, uiTheme, fp =>
|
|
703
|
-
uiTheme.getLangIcon(getLanguageFromPath(fp)),
|
|
704
|
-
);
|
|
705
|
-
if (diagText.trim()) {
|
|
706
|
-
const diagLines = diagText.split("\n");
|
|
707
|
-
const firstNonEmpty = diagLines.findIndex(line => line.trim());
|
|
708
|
-
if (firstNonEmpty >= 0) body += `\n${diagLines.slice(firstNonEmpty).join("\n")}`;
|
|
744
|
+
return framedToolCard(
|
|
745
|
+
uiTheme,
|
|
746
|
+
() => {
|
|
747
|
+
const { expanded } = options;
|
|
748
|
+
let body = renderContentPreview(fileContent, expanded, lang, uiTheme, previewCache);
|
|
749
|
+
if (isPartial && progressText) {
|
|
750
|
+
const safeProgressText = truncateToWidth(
|
|
751
|
+
replaceTabs(progressText),
|
|
752
|
+
TRUNCATE_LENGTHS.LINE,
|
|
753
|
+
Ellipsis.Unicode,
|
|
754
|
+
);
|
|
755
|
+
body = `${uiTheme.fg("muted", safeProgressText)}${body ? `\n${body}` : ""}`;
|
|
709
756
|
}
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
757
|
+
if (!isPartial && diagnostics) {
|
|
758
|
+
const diagText = formatDiagnostics(diagnostics, expanded, uiTheme, fp =>
|
|
759
|
+
uiTheme.getLangIcon(getLanguageFromPath(fp)),
|
|
760
|
+
);
|
|
761
|
+
if (diagText.trim()) {
|
|
762
|
+
const diagLines = diagText.split("\n");
|
|
763
|
+
const firstNonEmpty = diagLines.findIndex(line => line.trim());
|
|
764
|
+
if (firstNonEmpty >= 0) body += `\n${diagLines.slice(firstNonEmpty).join("\n")}`;
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
const bodyLines = body.split("\n");
|
|
768
|
+
while (bodyLines.length > 0 && bodyLines[0].trim() === "") bodyLines.shift();
|
|
769
|
+
return {
|
|
770
|
+
header,
|
|
771
|
+
sections: bodyLines.length > 0 ? [{ content: bodyLines }] : [],
|
|
772
|
+
phase: isPartial ? "partial" : "success",
|
|
773
|
+
borderColor: "borderMuted",
|
|
774
|
+
};
|
|
775
|
+
},
|
|
776
|
+
// No release hook: `previewCache` holds the width-independent highlighted preview, which resize replay
|
|
777
|
+
// would otherwise re-highlight for every committed card.
|
|
778
|
+
);
|
|
720
779
|
},
|
|
721
780
|
describeCall(
|
|
722
781
|
args: WriteRenderArgs,
|
|
@@ -739,17 +798,12 @@ export const writeToolRenderer = {
|
|
|
739
798
|
return describeXdevCall(xdev.name, args.content, options, options.renderContext?.resolveXdevMounted);
|
|
740
799
|
}
|
|
741
800
|
const content = normalizeDisplayText(args.content);
|
|
801
|
+
// A figure write draws as it arrives, over its streaming source.
|
|
802
|
+
const figure = writeFigure(args, undefined, options);
|
|
742
803
|
return {
|
|
743
804
|
tool: writeToolHead(rawPath, content),
|
|
744
|
-
body: content
|
|
745
|
-
|
|
746
|
-
code(content, {
|
|
747
|
-
lang: rawPath ? getLanguageFromPath(rawPath) : undefined,
|
|
748
|
-
numbers: true,
|
|
749
|
-
}),
|
|
750
|
-
]
|
|
751
|
-
: [],
|
|
752
|
-
preview: NATIVE_WRITE_PREVIEW,
|
|
805
|
+
body: content ? compact([describeFigure(figure), describeSource(rawPath, content)]) : [],
|
|
806
|
+
preview: figure ? { lines: NATIVE_FIGURE_PREVIEW_LINES } : NATIVE_WRITE_PREVIEW,
|
|
753
807
|
};
|
|
754
808
|
},
|
|
755
809
|
|
|
@@ -776,22 +830,20 @@ export const writeToolRenderer = {
|
|
|
776
830
|
});
|
|
777
831
|
if (result.isError) return { tool, tone: "error", body: [errorText(resultText(result))] };
|
|
778
832
|
const progressText = resultText(result);
|
|
779
|
-
// A
|
|
780
|
-
|
|
833
|
+
// A figure write leads with the drawing, drawn as its fence in a reply;
|
|
834
|
+
// its source follows below the collapsed clamp.
|
|
835
|
+
const figure = writeFigure(args, result, options);
|
|
781
836
|
const body = compact<NativeChild>([
|
|
782
837
|
isPartial &&
|
|
783
838
|
progressText.length > 0 &&
|
|
784
839
|
node("text", { spans: [span(progressText, "muted")], truncate: "end" }),
|
|
785
|
-
|
|
786
|
-
fileContent.length > 0 &&
|
|
787
|
-
code(fileContent, {
|
|
788
|
-
lang: rawPath ? getLanguageFromPath(rawPath) : undefined,
|
|
789
|
-
numbers: true,
|
|
790
|
-
}),
|
|
840
|
+
describeFigure(figure),
|
|
841
|
+
fileContent.length > 0 && describeSource(rawPath, fileContent),
|
|
791
842
|
diagnosticsSection(diagnostics),
|
|
792
843
|
]);
|
|
793
|
-
return { tool, body, preview:
|
|
844
|
+
return { tool, body, preview: figure ? { lines: NATIVE_FIGURE_PREVIEW_LINES } : NATIVE_WRITE_PREVIEW };
|
|
794
845
|
},
|
|
846
|
+
figure: writeFigure,
|
|
795
847
|
mergeCallAndResult: true,
|
|
796
848
|
// The collapsed pending preview follows the streaming edge with a tail
|
|
797
849
|
// window once the content outgrows it (`… (N earlier lines)` + last rows);
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Theme } from "../theme/theme";
|
|
2
|
+
import type { Component } from "../tui";
|
|
3
|
+
import type { NativeToolView, RenderResultContextOptions, RenderResultOptions, ToolRenderResult } from "./renderer";
|
|
4
|
+
import type { YieldItem } from "./task";
|
|
5
|
+
/** `yield` arguments as sent; strict-mode providers send omitted optionals as `null`. */
|
|
6
|
+
interface YieldRenderArgs {
|
|
7
|
+
type?: string | string[] | null;
|
|
8
|
+
data?: unknown;
|
|
9
|
+
error?: string | null;
|
|
10
|
+
/** 1-based workpool item number. */
|
|
11
|
+
key?: number;
|
|
12
|
+
}
|
|
13
|
+
/** Shows a `yield` call's submitted data instead of its model-facing acknowledgement. */
|
|
14
|
+
export declare const yieldToolRenderer: {
|
|
15
|
+
renderCall(args: YieldRenderArgs, options: RenderResultOptions, theme: Theme): Component;
|
|
16
|
+
renderResult(result: ToolRenderResult<YieldItem>, options: RenderResultContextOptions, theme: Theme, args?: YieldRenderArgs): Component;
|
|
17
|
+
describeCall(args: YieldRenderArgs, options: RenderResultOptions): NativeToolView;
|
|
18
|
+
describeResult(result: ToolRenderResult<YieldItem>, _options: RenderResultContextOptions, args?: YieldRenderArgs): NativeToolView;
|
|
19
|
+
mergeCallAndResult: true;
|
|
20
|
+
animatedPendingPreview: true;
|
|
21
|
+
};
|
|
22
|
+
export {};
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transcript renderer for the subagent `yield` tool ("Submit Result"). Its
|
|
3
|
+
* reply ("Result submitted.") is for the model; the card shows what was
|
|
4
|
+
* submitted: the section labels or workpool item in the head, the data as a
|
|
5
|
+
* JSON tree (Markdown when it is a string), and the reported or rejected error.
|
|
6
|
+
*/
|
|
7
|
+
import { formatCount, isRecord, sanitizeText } from "@linxiraos/pi-utils";
|
|
8
|
+
import { compact, md } from "../native/describe";
|
|
9
|
+
import { OwnerMemo } from "../native/memo";
|
|
10
|
+
import type { NativeChild } from "../native/node";
|
|
11
|
+
import { formatOutputPaneLines, styleToolOutputLine } from "../render/output-pane";
|
|
12
|
+
import { formatExpandHint, sanitizeDisplayLines, truncateToWidth } from "../render/render-utils";
|
|
13
|
+
import type { StatusLineOptions } from "../render/status-line";
|
|
14
|
+
import { plainToolCard, type ToolCardPhase } from "../render/tool-card";
|
|
15
|
+
import type { Theme } from "../theme/theme";
|
|
16
|
+
import type { Component } from "../tui";
|
|
17
|
+
import { wrapTextWithAnsi } from "../utils";
|
|
18
|
+
import {
|
|
19
|
+
describeJsonTree,
|
|
20
|
+
JSON_TREE_MAX_DEPTH_COLLAPSED,
|
|
21
|
+
JSON_TREE_MAX_DEPTH_EXPANDED,
|
|
22
|
+
JSON_TREE_MAX_LINES_COLLAPSED,
|
|
23
|
+
JSON_TREE_MAX_LINES_EXPANDED,
|
|
24
|
+
JSON_TREE_SCALAR_LEN_COLLAPSED,
|
|
25
|
+
JSON_TREE_SCALAR_LEN_EXPANDED,
|
|
26
|
+
renderJsonTreeLines,
|
|
27
|
+
} from "./json-tree";
|
|
28
|
+
import { errorText, noteText, resultText } from "./native-view";
|
|
29
|
+
import type {
|
|
30
|
+
NativeToolView,
|
|
31
|
+
RenderResultContextOptions,
|
|
32
|
+
RenderResultOptions,
|
|
33
|
+
ToolRenderer,
|
|
34
|
+
ToolRenderResult,
|
|
35
|
+
} from "./renderer";
|
|
36
|
+
import type { YieldItem } from "./task";
|
|
37
|
+
|
|
38
|
+
const TITLE = "Submit Result";
|
|
39
|
+
|
|
40
|
+
/** Collapsed rows of string data (a report) in the ANSI card. */
|
|
41
|
+
const TEXT_ROWS_COLLAPSED = 4;
|
|
42
|
+
|
|
43
|
+
/** `yield` arguments as sent; strict-mode providers send omitted optionals as `null`. */
|
|
44
|
+
interface YieldRenderArgs {
|
|
45
|
+
type?: string | string[] | null;
|
|
46
|
+
data?: unknown;
|
|
47
|
+
error?: string | null;
|
|
48
|
+
/** 1-based workpool item number. */
|
|
49
|
+
key?: number;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** One `yield` call as its card shows it, read from the args and, once settled, the result. */
|
|
53
|
+
interface Submission {
|
|
54
|
+
/** Section labels or workpool item the call fills; absent for a whole result. */
|
|
55
|
+
target?: string;
|
|
56
|
+
/** The accepted (normalized) data once settled, else the data as sent. */
|
|
57
|
+
data: unknown;
|
|
58
|
+
/** The failure the call reports, or why the tool rejected it. */
|
|
59
|
+
error?: string;
|
|
60
|
+
settled: boolean;
|
|
61
|
+
/** Accepted although it failed the output schema. */
|
|
62
|
+
overridden: boolean;
|
|
63
|
+
/** Where a settled call without data takes its result from. */
|
|
64
|
+
note?: string;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function readSubmission(
|
|
68
|
+
args: YieldRenderArgs | undefined,
|
|
69
|
+
result: ToolRenderResult<YieldItem> | undefined,
|
|
70
|
+
): Submission {
|
|
71
|
+
const details = result?.isError ? undefined : result?.details;
|
|
72
|
+
const type = details?.type ?? args?.type ?? undefined;
|
|
73
|
+
const incremental = Array.isArray(type) && type.length > 0;
|
|
74
|
+
const data = details?.data !== undefined ? details.data : (args?.data ?? undefined);
|
|
75
|
+
let error = args?.error || undefined;
|
|
76
|
+
if (result?.isError) error = resultText(result) || `${TITLE} failed`;
|
|
77
|
+
else if (result && details?.status === "aborted") error = details.error || resultText(result) || "Aborted";
|
|
78
|
+
const settled = result !== undefined;
|
|
79
|
+
return {
|
|
80
|
+
target: typeof args?.key === "number" ? `item ${args.key}` : incremental ? type.join(", ") : undefined,
|
|
81
|
+
data,
|
|
82
|
+
error,
|
|
83
|
+
settled,
|
|
84
|
+
overridden: details?.schemaOverridden === true,
|
|
85
|
+
note:
|
|
86
|
+
settled && error === undefined && data === undefined
|
|
87
|
+
? incremental
|
|
88
|
+
? "Takes the last message as this section"
|
|
89
|
+
: "Ends with the sections submitted earlier, or the last message"
|
|
90
|
+
: undefined,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Head fact for container data (`13 fields`, `4 items`). */
|
|
95
|
+
function sizeMeta(data: unknown): string | undefined {
|
|
96
|
+
if (Array.isArray(data)) return formatCount("item", data.length);
|
|
97
|
+
if (isRecord(data)) return formatCount("field", Object.keys(data).length);
|
|
98
|
+
return undefined;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function describeData(data: unknown, streaming: boolean): NativeChild | undefined {
|
|
102
|
+
if (data === undefined) return undefined;
|
|
103
|
+
if (typeof data === "string") return data.trim() ? md(data, { stream: streaming }) : noteText("(empty)");
|
|
104
|
+
// Open to the depth cap: the data is the card's whole point, and the
|
|
105
|
+
// collapsed card's preview clamp already bounds what shows.
|
|
106
|
+
return describeJsonTree(data, { hiddenRootKeys: [], openDepth: JSON_TREE_MAX_DEPTH_EXPANDED });
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function describeSubmission(s: Submission, streaming: boolean): NativeToolView {
|
|
110
|
+
const size = sizeMeta(s.data);
|
|
111
|
+
return {
|
|
112
|
+
tool: {
|
|
113
|
+
title: TITLE,
|
|
114
|
+
target: s.target,
|
|
115
|
+
targetKind: s.target ? "text" : undefined,
|
|
116
|
+
meta: size ? [size] : undefined,
|
|
117
|
+
badges: s.overridden ? [{ text: "schema overridden", tone: "warning" }] : undefined,
|
|
118
|
+
},
|
|
119
|
+
tone: s.settled && s.error !== undefined ? "error" : undefined,
|
|
120
|
+
body: compact([
|
|
121
|
+
s.error !== undefined ? errorText(s.error) : undefined,
|
|
122
|
+
describeData(s.data, streaming),
|
|
123
|
+
s.note ? noteText(s.note, "dim") : undefined,
|
|
124
|
+
]),
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function dataLines(data: unknown, expanded: boolean, width: number, theme: Theme): string[] {
|
|
129
|
+
if (data === undefined) return [];
|
|
130
|
+
if (typeof data === "string") {
|
|
131
|
+
return [
|
|
132
|
+
...formatOutputPaneLines(
|
|
133
|
+
{
|
|
134
|
+
lines: sanitizeText(data).trimEnd().split("\n"),
|
|
135
|
+
expanded,
|
|
136
|
+
collapsedMaxLines: TEXT_ROWS_COLLAPSED,
|
|
137
|
+
styleLine: line => truncateToWidth(styleToolOutputLine(line, theme), width),
|
|
138
|
+
},
|
|
139
|
+
theme,
|
|
140
|
+
).lines,
|
|
141
|
+
];
|
|
142
|
+
}
|
|
143
|
+
const tree = renderJsonTreeLines(data, theme, {
|
|
144
|
+
maxDepth: expanded ? JSON_TREE_MAX_DEPTH_EXPANDED : JSON_TREE_MAX_DEPTH_COLLAPSED,
|
|
145
|
+
maxLines: expanded ? JSON_TREE_MAX_LINES_EXPANDED : JSON_TREE_MAX_LINES_COLLAPSED,
|
|
146
|
+
maxScalarLen: expanded ? JSON_TREE_SCALAR_LEN_EXPANDED : JSON_TREE_SCALAR_LEN_COLLAPSED,
|
|
147
|
+
sanitizeText,
|
|
148
|
+
hiddenRootKeys: [],
|
|
149
|
+
});
|
|
150
|
+
if (tree.truncated) tree.lines.push(expanded ? theme.fg("dim", "…") : formatExpandHint(theme, false, true));
|
|
151
|
+
return tree.lines;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function renderSubmission(s: Submission, options: RenderResultOptions, theme: Theme): Component {
|
|
155
|
+
// The body is spinner-invariant; rebuild it only on expansion or width change.
|
|
156
|
+
let memo: { expanded: boolean; width: number; lines: readonly string[] } | undefined;
|
|
157
|
+
return plainToolCard(
|
|
158
|
+
theme,
|
|
159
|
+
({ contentWidth }) => {
|
|
160
|
+
const running = options.spinnerFrame !== undefined;
|
|
161
|
+
const failed = s.settled && s.error !== undefined;
|
|
162
|
+
const size = sizeMeta(s.data);
|
|
163
|
+
const status: StatusLineOptions = {
|
|
164
|
+
icon: !s.settled ? (running ? "running" : "pending") : failed ? "error" : "done",
|
|
165
|
+
spinnerFrame: options.spinnerFrame,
|
|
166
|
+
title: TITLE,
|
|
167
|
+
description: s.target,
|
|
168
|
+
badge: s.overridden ? { label: "schema overridden", color: "warning" } : undefined,
|
|
169
|
+
meta: size ? [size] : undefined,
|
|
170
|
+
};
|
|
171
|
+
const phase: ToolCardPhase = !s.settled ? (running ? "running" : "partial") : failed ? "error" : "success";
|
|
172
|
+
if (memo === undefined || memo.expanded !== options.expanded || memo.width !== contentWidth) {
|
|
173
|
+
const lines: string[] = [];
|
|
174
|
+
// Wrapped in full: a schema rejection names every offending field.
|
|
175
|
+
if (s.error !== undefined) {
|
|
176
|
+
for (const line of sanitizeDisplayLines(s.error)) {
|
|
177
|
+
lines.push(...wrapTextWithAnsi(theme.fg("error", line), Math.max(1, contentWidth)));
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
lines.push(...dataLines(s.data, options.expanded, contentWidth, theme));
|
|
181
|
+
if (s.note) lines.push(theme.fg("dim", s.note));
|
|
182
|
+
memo = { expanded: options.expanded, width: contentWidth, lines };
|
|
183
|
+
}
|
|
184
|
+
return { status, phase, body: memo.lines };
|
|
185
|
+
},
|
|
186
|
+
{ paddingX: 1, paddingY: 1, ignoreTight: true, onInvalidate: () => (memo = undefined) },
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const callMemo = new OwnerMemo<NativeToolView>();
|
|
191
|
+
const resultMemo = new OwnerMemo<NativeToolView>();
|
|
192
|
+
|
|
193
|
+
/** Shows a `yield` call's submitted data instead of its model-facing acknowledgement. */
|
|
194
|
+
export const yieldToolRenderer = {
|
|
195
|
+
renderCall(args: YieldRenderArgs, options: RenderResultOptions, theme: Theme): Component {
|
|
196
|
+
return renderSubmission(readSubmission(args, undefined), options, theme);
|
|
197
|
+
},
|
|
198
|
+
|
|
199
|
+
renderResult(
|
|
200
|
+
result: ToolRenderResult<YieldItem>,
|
|
201
|
+
options: RenderResultContextOptions,
|
|
202
|
+
theme: Theme,
|
|
203
|
+
args?: YieldRenderArgs,
|
|
204
|
+
): Component {
|
|
205
|
+
return renderSubmission(readSubmission(args, result), options, theme);
|
|
206
|
+
},
|
|
207
|
+
|
|
208
|
+
describeCall(args: YieldRenderArgs, options: RenderResultOptions): NativeToolView {
|
|
209
|
+
const streaming = options.argsComplete === false;
|
|
210
|
+
return callMemo.get(args, [streaming], () => describeSubmission(readSubmission(args, undefined), streaming));
|
|
211
|
+
},
|
|
212
|
+
|
|
213
|
+
describeResult(
|
|
214
|
+
result: ToolRenderResult<YieldItem>,
|
|
215
|
+
_options: RenderResultContextOptions,
|
|
216
|
+
args?: YieldRenderArgs,
|
|
217
|
+
): NativeToolView {
|
|
218
|
+
return resultMemo.get(result.content, [args, result.details, result.isError], () =>
|
|
219
|
+
describeSubmission(readSubmission(args, result), false),
|
|
220
|
+
);
|
|
221
|
+
},
|
|
222
|
+
|
|
223
|
+
mergeCallAndResult: true,
|
|
224
|
+
animatedPendingPreview: true,
|
|
225
|
+
} satisfies ToolRenderer<YieldRenderArgs, YieldItem>;
|
package/src/tui.d.ts
CHANGED
|
@@ -78,6 +78,10 @@ export interface TUIStartOptions {
|
|
|
78
78
|
* Paint without owning stdin: the terminal stays in cooked mode (kernel
|
|
79
79
|
* echo + line editing at the hardware cursor) until {@link TUI.enableInput}
|
|
80
80
|
* switches to raw input and replays the kernel-buffered keystrokes.
|
|
81
|
+
*
|
|
82
|
+
* A terminal expected to speak TSP needs raw input from the start, so it
|
|
83
|
+
* gets it; its keystrokes are held instead (TSP events and the cell-size
|
|
84
|
+
* reply still apply) until {@link TUI.releaseHeldInput} replays them.
|
|
81
85
|
*/
|
|
82
86
|
deferInput?: boolean;
|
|
83
87
|
}
|
|
@@ -169,6 +173,16 @@ export interface Component {
|
|
|
169
173
|
* Called when theme changes or when component needs to re-render from scratch.
|
|
170
174
|
*/
|
|
171
175
|
invalidate?(): void;
|
|
176
|
+
/**
|
|
177
|
+
* Optional hook to drop memoized render output (rows, parse and wrap state)
|
|
178
|
+
* that the next `render()` can rebuild from state the component keeps.
|
|
179
|
+
* Unlike {@link invalidate}, it must not rebuild anything eagerly: no
|
|
180
|
+
* renderer or extension callbacks, no image conversions, and no child
|
|
181
|
+
* replacement or disposal. The next render must return the same rows it
|
|
182
|
+
* would have returned without the release. Components without it are
|
|
183
|
+
* simply not released.
|
|
184
|
+
*/
|
|
185
|
+
releaseRenderCaches?(): void;
|
|
172
186
|
/**
|
|
173
187
|
* Optional hook to set whether this component ignores tight layout mode.
|
|
174
188
|
*/
|
|
@@ -308,6 +322,7 @@ export declare class Container implements Component {
|
|
|
308
322
|
/** Dispose every child, then detach it from this container. */
|
|
309
323
|
disposeChildren(): void;
|
|
310
324
|
invalidate(): void;
|
|
325
|
+
releaseRenderCaches(): void;
|
|
311
326
|
/**
|
|
312
327
|
* Propagate teardown to children. Call when the container's children are
|
|
313
328
|
* being permanently discarded (not when they are detached for reuse — use
|
|
@@ -476,6 +491,22 @@ export declare class TUI extends Container {
|
|
|
476
491
|
* path. Idempotent; no-op when input was never deferred.
|
|
477
492
|
*/
|
|
478
493
|
enableInput(): void;
|
|
494
|
+
/**
|
|
495
|
+
* Replay the keystrokes held since a TSP `deferInput` start through the
|
|
496
|
+
* normal input path, then deliver input live. Call once the app's key
|
|
497
|
+
* handlers are installed so a hotkey pressed during startup still fires.
|
|
498
|
+
* Only keys typed while the start-time focus owner had focus are held. The
|
|
499
|
+
* hold survives a stop/start until released. Idempotent; no-op when nothing
|
|
500
|
+
* is held.
|
|
501
|
+
*/
|
|
502
|
+
releaseHeldInput(): void;
|
|
503
|
+
/**
|
|
504
|
+
* Hand held-key ownership from `previous` to `next` when the app replaces
|
|
505
|
+
* the component focused at start (a swapped-in custom editor), so keys
|
|
506
|
+
* typed into the replacement stay queued behind the held ones instead of
|
|
507
|
+
* overtaking them. No-op unless `previous` owns the held keys.
|
|
508
|
+
*/
|
|
509
|
+
replaceHeldFocus(previous: Component, next: Component): void;
|
|
479
510
|
addStartListener(listener: StartListener): () => void;
|
|
480
511
|
addInputListener(listener: InputListener): () => void;
|
|
481
512
|
removeInputListener(listener: InputListener): void;
|