@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/fetch.ts
CHANGED
|
@@ -282,6 +282,10 @@ export function renderReadUrlResult(
|
|
|
282
282
|
|
|
283
283
|
let lastExpanded: boolean | undefined;
|
|
284
284
|
let contentPreviewLines: string[] | undefined;
|
|
285
|
+
const dropPreview = () => {
|
|
286
|
+
lastExpanded = undefined;
|
|
287
|
+
contentPreviewLines = undefined;
|
|
288
|
+
};
|
|
285
289
|
return framedToolCard(
|
|
286
290
|
uiTheme,
|
|
287
291
|
() => {
|
|
@@ -315,11 +319,6 @@ export function renderReadUrlResult(
|
|
|
315
319
|
applyBg: false,
|
|
316
320
|
};
|
|
317
321
|
},
|
|
318
|
-
{
|
|
319
|
-
onInvalidate: () => {
|
|
320
|
-
lastExpanded = undefined;
|
|
321
|
-
contentPreviewLines = undefined;
|
|
322
|
-
},
|
|
323
|
-
},
|
|
322
|
+
{ onInvalidate: dropPreview, onReleaseRenderCaches: dropPreview },
|
|
324
323
|
);
|
|
325
324
|
}
|
package/src/tools/glob.d.ts
CHANGED
|
@@ -12,6 +12,10 @@ export interface GlobToolDetails {
|
|
|
12
12
|
fileCount?: number;
|
|
13
13
|
files?: string[];
|
|
14
14
|
truncated?: boolean;
|
|
15
|
+
/** The scan hit the tool deadline, so the listed files are an incomplete
|
|
16
|
+
* set rather than a complete answer. Distinct from `truncated`, which also
|
|
17
|
+
* covers result-limit and output truncation. */
|
|
18
|
+
timedOut?: boolean;
|
|
15
19
|
error?: string;
|
|
16
20
|
/** Working directory at search time. Used by the renderer to resolve relative
|
|
17
21
|
* file paths to absolute paths for OSC 8 hyperlinks. */
|
package/src/tools/glob.ts
CHANGED
|
@@ -18,7 +18,6 @@ import {
|
|
|
18
18
|
PREVIEW_LIMITS,
|
|
19
19
|
} from "../render/render-utils";
|
|
20
20
|
import { formatFullOutputReference } from "./output-meta";
|
|
21
|
-
import { tuiText, tuiTextFmt } from "../i18n";
|
|
22
21
|
|
|
23
22
|
/** Display metadata for glob tool results. */
|
|
24
23
|
export interface GlobToolDetails {
|
|
@@ -30,6 +29,10 @@ export interface GlobToolDetails {
|
|
|
30
29
|
fileCount?: number;
|
|
31
30
|
files?: string[];
|
|
32
31
|
truncated?: boolean;
|
|
32
|
+
/** The scan hit the tool deadline, so the listed files are an incomplete
|
|
33
|
+
* set rather than a complete answer. Distinct from `truncated`, which also
|
|
34
|
+
* covers result-limit and output truncation. */
|
|
35
|
+
timedOut?: boolean;
|
|
33
36
|
error?: string;
|
|
34
37
|
/** Working directory at search time. Used by the renderer to resolve relative
|
|
35
38
|
* file paths to absolute paths for OSC 8 hyperlinks. */
|
|
@@ -134,10 +137,7 @@ export const globToolRenderer = {
|
|
|
134
137
|
const details = result.details;
|
|
135
138
|
|
|
136
139
|
if (result.isError || details?.error) {
|
|
137
|
-
const errorText =
|
|
138
|
-
details?.error ||
|
|
139
|
-
result.content?.find(c => c.type === "text")?.text ||
|
|
140
|
-
tuiText("glUnknownError", "Unknown error");
|
|
140
|
+
const errorText = details?.error || result.content?.find(c => c.type === "text")?.text || "Unknown error";
|
|
141
141
|
return new Text(formatErrorMessage(errorText, uiTheme), 1, 0);
|
|
142
142
|
}
|
|
143
143
|
|
|
@@ -151,7 +151,7 @@ export const globToolRenderer = {
|
|
|
151
151
|
textContent.includes("No files found") ||
|
|
152
152
|
textContent.trim() === ""
|
|
153
153
|
) {
|
|
154
|
-
return new Text(formatEmptyMessage(
|
|
154
|
+
return new Text(formatEmptyMessage("No files found", uiTheme), 1, 0);
|
|
155
155
|
}
|
|
156
156
|
|
|
157
157
|
const lines = textContent.split("\n").filter(l => l.trim());
|
|
@@ -161,10 +161,7 @@ export const globToolRenderer = {
|
|
|
161
161
|
title: "Glob",
|
|
162
162
|
titleColor: "toolTitle",
|
|
163
163
|
description: formatGlobRenderPaths(args),
|
|
164
|
-
meta:
|
|
165
|
-
lines.length === 1
|
|
166
|
-
? [tuiText("globFileOne", "1 file")]
|
|
167
|
-
: [tuiTextFmt("globFileManyFmt", "%d files", lines.length)],
|
|
164
|
+
meta: [formatCount("file", lines.length)],
|
|
168
165
|
},
|
|
169
166
|
uiTheme,
|
|
170
167
|
);
|
|
@@ -191,36 +188,30 @@ export const globToolRenderer = {
|
|
|
191
188
|
const truncation = details?.truncation ?? details?.meta?.truncation;
|
|
192
189
|
const limits = details?.meta?.limits;
|
|
193
190
|
const truncated = Boolean(details?.truncated || truncation || details?.resultLimitReached || limits?.resultLimit);
|
|
191
|
+
// A non-empty timed-out result is the case `truncated` alone cannot
|
|
192
|
+
// explain: the listing is short because the scan died, not because it
|
|
193
|
+
// finished. Say which one it was.
|
|
194
|
+
const timedOut = Boolean(details?.timedOut);
|
|
194
195
|
const files = details?.files ?? [];
|
|
195
196
|
|
|
196
197
|
const missingPaths = details?.missingPaths ?? [];
|
|
197
198
|
const missingNote =
|
|
198
|
-
missingPaths.length > 0
|
|
199
|
-
? uiTheme.fg(
|
|
200
|
-
"warning",
|
|
201
|
-
tuiTextFmt(
|
|
202
|
-
"gpSkippedMissingFmt",
|
|
203
|
-
`skipped missing: ${missingPaths.join(", ")}`,
|
|
204
|
-
missingPaths.join(", "),
|
|
205
|
-
),
|
|
206
|
-
)
|
|
207
|
-
: undefined;
|
|
199
|
+
missingPaths.length > 0 ? uiTheme.fg("warning", `skipped missing: ${missingPaths.join(", ")}`) : undefined;
|
|
208
200
|
|
|
209
201
|
if (fileCount === 0) {
|
|
210
202
|
// `truncated` on an empty result means the scan timed out mid-walk —
|
|
211
|
-
// render "incomplete", not a definitive "No files found".
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
203
|
+
// render "incomplete", not a definitive "No files found". `timedOut`
|
|
204
|
+
// states it outright; the inference stays for old transcripts that
|
|
205
|
+
// predate the field.
|
|
206
|
+
const emptyTimedOut = timedOut || truncated;
|
|
207
|
+
const emptyLabel = emptyTimedOut ? "No matches before timeout (scan incomplete)" : "No files found";
|
|
215
208
|
const header = renderStatusLine(
|
|
216
209
|
{
|
|
217
210
|
icon: "warning",
|
|
218
211
|
title: "Glob",
|
|
219
212
|
titleColor: "toolTitle",
|
|
220
213
|
description: formatGlobRenderPaths(args),
|
|
221
|
-
meta:
|
|
222
|
-
? [tuiText("globZeroFiles", "0 files"), uiTheme.fg("warning", tuiText("globTimedOut", "timed out"))]
|
|
223
|
-
: [tuiText("globZeroFiles", "0 files")],
|
|
214
|
+
meta: emptyTimedOut ? ["0 files", uiTheme.fg("warning", "timed out")] : ["0 files"],
|
|
224
215
|
},
|
|
225
216
|
uiTheme,
|
|
226
217
|
);
|
|
@@ -228,10 +219,9 @@ export const globToolRenderer = {
|
|
|
228
219
|
if (missingNote) lines.push(missingNote);
|
|
229
220
|
return new Text(lines.join("\n"), 1, 0);
|
|
230
221
|
}
|
|
231
|
-
const meta: string[] =
|
|
232
|
-
|
|
233
|
-
if (
|
|
234
|
-
if (truncated) meta.push(uiTheme.fg("warning", tuiText("globTruncated", "truncated")));
|
|
222
|
+
const meta: string[] = [formatCount("file", fileCount)];
|
|
223
|
+
if (details?.scopePath) meta.push(`in ${details.scopePath}`);
|
|
224
|
+
if (truncated) meta.push(uiTheme.fg("warning", timedOut ? "timed out" : "truncated"));
|
|
235
225
|
const header = renderStatusLine(
|
|
236
226
|
{
|
|
237
227
|
...(truncated ? { icon: "warning" as const } : { iconOverride: globStatusIcon(uiTheme) }),
|
|
@@ -244,43 +234,15 @@ export const globToolRenderer = {
|
|
|
244
234
|
);
|
|
245
235
|
|
|
246
236
|
const truncationReasons: string[] = [];
|
|
247
|
-
if (details?.resultLimitReached)
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
"globLimitResultsFmt",
|
|
251
|
-
`limit ${details.resultLimitReached} results`,
|
|
252
|
-
details.resultLimitReached,
|
|
253
|
-
),
|
|
254
|
-
);
|
|
255
|
-
if (limits?.resultLimit)
|
|
256
|
-
truncationReasons.push(
|
|
257
|
-
tuiTextFmt(
|
|
258
|
-
"globLimitResultsFmt",
|
|
259
|
-
`limit ${limits.resultLimit.reached} results`,
|
|
260
|
-
limits.resultLimit.reached,
|
|
261
|
-
),
|
|
262
|
-
);
|
|
263
|
-
if (truncation)
|
|
264
|
-
truncationReasons.push(
|
|
265
|
-
truncation.truncatedBy === "lines"
|
|
266
|
-
? tuiText("globLineLimit", "line limit")
|
|
267
|
-
: tuiText("globSizeLimit", "size limit"),
|
|
268
|
-
);
|
|
237
|
+
if (details?.resultLimitReached) truncationReasons.push(`limit ${details.resultLimitReached} results`);
|
|
238
|
+
if (limits?.resultLimit) truncationReasons.push(`limit ${limits.resultLimit.reached} results`);
|
|
239
|
+
if (truncation) truncationReasons.push(truncation.truncatedBy === "lines" ? "line limit" : "size limit");
|
|
269
240
|
const artifactId = truncation && "artifactId" in truncation ? truncation.artifactId : undefined;
|
|
270
241
|
if (artifactId) truncationReasons.push(formatFullOutputReference(artifactId));
|
|
271
242
|
|
|
272
243
|
const extraLines: string[] = [];
|
|
273
244
|
if (truncationReasons.length > 0) {
|
|
274
|
-
extraLines.push(
|
|
275
|
-
uiTheme.fg(
|
|
276
|
-
"warning",
|
|
277
|
-
tuiTextFmt(
|
|
278
|
-
"globTruncatedReasonsFmt",
|
|
279
|
-
`truncated: ${truncationReasons.join(", ")}`,
|
|
280
|
-
truncationReasons.join(", "),
|
|
281
|
-
),
|
|
282
|
-
),
|
|
283
|
-
);
|
|
245
|
+
extraLines.push(uiTheme.fg("warning", `truncated: ${truncationReasons.join(", ")}`));
|
|
284
246
|
}
|
|
285
247
|
if (missingNote) extraLines.push(missingNote);
|
|
286
248
|
|
|
@@ -343,6 +305,7 @@ export const globToolRenderer = {
|
|
|
343
305
|
const truncation = details.truncation ?? details.meta?.truncation;
|
|
344
306
|
const limits = details.meta?.limits;
|
|
345
307
|
const truncated = Boolean(details.truncated || truncation || details.resultLimitReached || limits?.resultLimit);
|
|
308
|
+
const timedOut = Boolean(details.timedOut);
|
|
346
309
|
const missingPaths = details.missingPaths ?? [];
|
|
347
310
|
const missingNote = missingPaths.length > 0 ? `skipped missing: ${missingPaths.join(", ")}` : undefined;
|
|
348
311
|
const scope = details.scopePath ? `in ${details.scopePath}` : undefined;
|
|
@@ -350,7 +313,10 @@ export const globToolRenderer = {
|
|
|
350
313
|
const foot = footnoteText(compact([missingNote]));
|
|
351
314
|
return {
|
|
352
315
|
// `truncated` on an empty result means the scan timed out mid-walk.
|
|
353
|
-
tool: {
|
|
316
|
+
tool: {
|
|
317
|
+
...globNativeHead(args, compact(["0 files", scope])),
|
|
318
|
+
note: timedOut || truncated ? "timed out" : undefined,
|
|
319
|
+
},
|
|
354
320
|
tone: "warning",
|
|
355
321
|
inline: true,
|
|
356
322
|
body: foot ? [foot] : undefined,
|
|
@@ -364,7 +330,9 @@ export const globToolRenderer = {
|
|
|
364
330
|
if (artifactId) reasons.push(formatFullOutputReference(artifactId));
|
|
365
331
|
const head = globNativeHead(args, compact([formatCount("file", details.fileCount), scope]));
|
|
366
332
|
return {
|
|
367
|
-
tool: truncated
|
|
333
|
+
tool: truncated
|
|
334
|
+
? { ...head, badges: [{ text: timedOut ? "timed out" : "truncated", tone: "warning" }] }
|
|
335
|
+
: head,
|
|
368
336
|
inline: true,
|
|
369
337
|
body: compact<NativeChild>([
|
|
370
338
|
describeGlobFiles(details.files ?? [], details.cwd),
|
package/src/tools/grep.ts
CHANGED
|
@@ -112,11 +112,6 @@ function parseSearchDisplayLineNumber(line: string): number | undefined {
|
|
|
112
112
|
|
|
113
113
|
const SEARCH_MATCH_LINE_RE = /^\s*\*\d+(?:│|[:|])/;
|
|
114
114
|
|
|
115
|
-
interface RenderedSearchLine {
|
|
116
|
-
raw: string;
|
|
117
|
-
styled: string;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
115
|
function isSearchMatchLine(line: string): boolean {
|
|
121
116
|
return SEARCH_MATCH_LINE_RE.test(line);
|
|
122
117
|
}
|
|
@@ -127,61 +122,91 @@ function isSearchHeaderLine(line: string): boolean {
|
|
|
127
122
|
|
|
128
123
|
const URL_HEADER_PREFIX_RE = /^#+\s+/;
|
|
129
124
|
|
|
130
|
-
|
|
125
|
+
/**
|
|
126
|
+
* Build a memoized per-line styler for search display output. Classification
|
|
127
|
+
* and URL-target tracking walk the whole output once (nested directory stacks
|
|
128
|
+
* span blank-line groups), but styling and hyperlinking run only for the rows
|
|
129
|
+
* a preview actually shows.
|
|
130
|
+
*/
|
|
131
|
+
function createSearchLineStyler(
|
|
131
132
|
lines: readonly string[],
|
|
132
133
|
headerBase: string | undefined,
|
|
133
134
|
fileScope: string | undefined,
|
|
134
135
|
uiTheme: Theme,
|
|
135
136
|
displayTargets: Record<string, string> | undefined,
|
|
136
|
-
):
|
|
137
|
+
): (index: number) => string {
|
|
137
138
|
const contexts = classifyGroupedLines(lines, headerBase, fileScope);
|
|
138
139
|
// `classifyGroupedLines` can't resolve internal URLs (TUI-only), so track the
|
|
139
140
|
// resolved URL target here and use it for the body lines that follow.
|
|
141
|
+
const urlRaw: (string | undefined)[] = Array.from({ length: lines.length }, () => undefined);
|
|
142
|
+
const urlFiles: (string | undefined)[] = Array.from({ length: lines.length }, () => undefined);
|
|
140
143
|
let urlFile: string | undefined;
|
|
141
|
-
|
|
144
|
+
for (let index = 0; index < lines.length; index++) {
|
|
142
145
|
const ctx = contexts[index]!;
|
|
143
146
|
if (ctx.kind === "dir") {
|
|
144
147
|
urlFile = undefined;
|
|
145
|
-
|
|
146
|
-
return { raw: line, styled: ctx.headerPath ? fileHyperlink(ctx.headerPath, styled) : styled };
|
|
147
|
-
}
|
|
148
|
-
if (ctx.kind === "file") {
|
|
148
|
+
} else if (ctx.kind === "file") {
|
|
149
149
|
if (ctx.isUrl) {
|
|
150
|
-
const raw =
|
|
151
|
-
.replace(URL_HEADER_PREFIX_RE, "")
|
|
150
|
+
const raw = lines[index]!.replace(URL_HEADER_PREFIX_RE, "")
|
|
152
151
|
.trimEnd()
|
|
153
152
|
.replace(/\s+\([^)]*\)\s*$/, "");
|
|
154
|
-
|
|
155
|
-
urlFile =
|
|
156
|
-
|
|
153
|
+
urlRaw[index] = raw;
|
|
154
|
+
urlFile = displayTargets?.[raw];
|
|
155
|
+
} else {
|
|
156
|
+
urlFile = undefined;
|
|
157
157
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
const styled = uiTheme.fg(ctx.depth === 1 ? "accent" : "dim", line);
|
|
161
|
-
return { raw: line, styled: ctx.headerPath ? fileHyperlink(ctx.headerPath, styled) : styled };
|
|
158
|
+
} else {
|
|
159
|
+
urlFiles[index] = urlFile;
|
|
162
160
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
161
|
+
}
|
|
162
|
+
const styledLines: (string | undefined)[] = Array.from({ length: lines.length }, () => undefined);
|
|
163
|
+
return index => {
|
|
164
|
+
const cached = styledLines[index];
|
|
165
|
+
if (cached !== undefined) return cached;
|
|
166
|
+
const line = lines[index]!;
|
|
167
|
+
const ctx = contexts[index]!;
|
|
168
|
+
let styled: string;
|
|
169
|
+
if (ctx.kind === "dir") {
|
|
170
|
+
const accent = uiTheme.fg("accent", line);
|
|
171
|
+
styled = ctx.headerPath ? fileHyperlink(ctx.headerPath, accent) : accent;
|
|
172
|
+
} else if (ctx.kind === "file") {
|
|
173
|
+
const raw = urlRaw[index];
|
|
174
|
+
if (raw !== undefined) {
|
|
175
|
+
styled = linkUrlLikeSearchHeader(raw, uiTheme.fg("accent", line), displayTargets?.[raw]).line;
|
|
176
|
+
} else {
|
|
177
|
+
// Root-level files keep the bright accent; nested file headers are dimmed.
|
|
178
|
+
const tinted = uiTheme.fg(ctx.depth === 1 ? "accent" : "dim", line);
|
|
179
|
+
styled = ctx.headerPath ? fileHyperlink(ctx.headerPath, tinted) : tinted;
|
|
180
|
+
}
|
|
181
|
+
} else {
|
|
182
|
+
const tinted = uiTheme.fg("toolOutput", line);
|
|
183
|
+
const lineNumber = parseSearchDisplayLineNumber(line);
|
|
184
|
+
const filePath = ctx.filePath ?? urlFiles[index];
|
|
185
|
+
styled = filePath && lineNumber !== undefined ? fileHyperlink(filePath, tinted, { line: lineNumber }) : tinted;
|
|
186
|
+
}
|
|
187
|
+
styledLines[index] = styled;
|
|
188
|
+
return styled;
|
|
189
|
+
};
|
|
171
190
|
}
|
|
172
191
|
|
|
173
|
-
function compactSearchPreviewGroup(group:
|
|
174
|
-
const compact = group.filter(
|
|
192
|
+
function compactSearchPreviewGroup(group: readonly number[], lines: readonly string[]): readonly number[] {
|
|
193
|
+
const compact = group.filter(index => isSearchHeaderLine(lines[index]!) || isSearchMatchLine(lines[index]!));
|
|
175
194
|
return compact.length > 0 ? compact : group;
|
|
176
195
|
}
|
|
177
196
|
|
|
178
|
-
function countPreviewMatches(lines: readonly
|
|
179
|
-
|
|
180
|
-
|
|
197
|
+
function countPreviewMatches(group: readonly number[], lines: readonly string[], hasMarkedMatches: boolean): number {
|
|
198
|
+
let count = 0;
|
|
199
|
+
for (const index of group) {
|
|
200
|
+
const line = lines[index]!;
|
|
201
|
+
if (hasMarkedMatches ? isSearchMatchLine(line) : !isSearchHeaderLine(line) && line.length > 0) count++;
|
|
202
|
+
}
|
|
203
|
+
return count;
|
|
181
204
|
}
|
|
182
205
|
|
|
183
206
|
function renderBudgetedSearchGroups(
|
|
184
|
-
groups:
|
|
207
|
+
groups: readonly (readonly number[])[],
|
|
208
|
+
lines: readonly string[],
|
|
209
|
+
styleLine: (index: number) => string,
|
|
185
210
|
maxLines: number,
|
|
186
211
|
matchCount: number,
|
|
187
212
|
uiTheme: Theme,
|
|
@@ -189,7 +214,7 @@ function renderBudgetedSearchGroups(
|
|
|
189
214
|
): string[] {
|
|
190
215
|
if (maxLines <= 0) return [];
|
|
191
216
|
const renderedGroups = groups
|
|
192
|
-
.map(group => (compact ? compactSearchPreviewGroup(group) : group))
|
|
217
|
+
.map(group => (compact ? compactSearchPreviewGroup(group, lines) : group))
|
|
193
218
|
.filter(group => group.length > 0);
|
|
194
219
|
if (renderedGroups.length === 0) return [];
|
|
195
220
|
|
|
@@ -198,13 +223,13 @@ function renderBudgetedSearchGroups(
|
|
|
198
223
|
let totalFallbackMatches = 0;
|
|
199
224
|
for (const group of renderedGroups) {
|
|
200
225
|
totalLines += group.length;
|
|
201
|
-
totalMarkedMatches += countPreviewMatches(group, true);
|
|
202
|
-
totalFallbackMatches += countPreviewMatches(group, false);
|
|
226
|
+
totalMarkedMatches += countPreviewMatches(group, lines, true);
|
|
227
|
+
totalFallbackMatches += countPreviewMatches(group, lines, false);
|
|
203
228
|
}
|
|
204
229
|
const hasMarkedMatches = totalMarkedMatches > 0;
|
|
205
230
|
const needsSummary = totalLines > maxLines;
|
|
206
231
|
const contentBudget = needsSummary ? Math.max(maxLines - 1, 0) : maxLines;
|
|
207
|
-
const visibleGroups:
|
|
232
|
+
const visibleGroups: (readonly number[])[] = [];
|
|
208
233
|
let visibleLineCount = 0;
|
|
209
234
|
let visibleMatches = 0;
|
|
210
235
|
for (const group of renderedGroups) {
|
|
@@ -215,30 +240,30 @@ function renderBudgetedSearchGroups(
|
|
|
215
240
|
const visibleGroup = group.slice(0, take);
|
|
216
241
|
visibleGroups.push(visibleGroup);
|
|
217
242
|
visibleLineCount += visibleGroup.length;
|
|
218
|
-
visibleMatches += countPreviewMatches(visibleGroup, hasMarkedMatches);
|
|
243
|
+
visibleMatches += countPreviewMatches(visibleGroup, lines, hasMarkedMatches);
|
|
219
244
|
}
|
|
220
245
|
|
|
221
246
|
const totalMatches = hasMarkedMatches ? totalMarkedMatches : Math.max(matchCount, totalFallbackMatches);
|
|
222
247
|
const hiddenMatches = Math.max(totalMatches - visibleMatches, 0);
|
|
223
248
|
const hiddenLines = Math.max(totalLines - visibleLineCount, 0);
|
|
224
249
|
const hasSummary = needsSummary && (hiddenMatches > 0 || hiddenLines > 0);
|
|
225
|
-
const
|
|
250
|
+
const out: string[] = [];
|
|
226
251
|
for (let i = 0; i < visibleGroups.length; i++) {
|
|
227
252
|
const group = visibleGroups[i]!;
|
|
228
253
|
const isLast = !hasSummary && i === visibleGroups.length - 1;
|
|
229
254
|
const prefix = `${uiTheme.fg("dim", getTreeBranch(isLast, uiTheme))} `;
|
|
230
255
|
const continuePrefix = uiTheme.fg("dim", getTreeContinuePrefix(isLast, uiTheme));
|
|
231
|
-
|
|
256
|
+
out.push(`${prefix}${replaceTabs(styleLine(group[0]!))}`);
|
|
232
257
|
for (let j = 1; j < group.length; j++) {
|
|
233
|
-
|
|
258
|
+
out.push(`${continuePrefix}${replaceTabs(styleLine(group[j]!))}`);
|
|
234
259
|
}
|
|
235
260
|
}
|
|
236
261
|
if (hasSummary) {
|
|
237
262
|
const hiddenLabel =
|
|
238
263
|
hiddenMatches > 0 ? formatMoreItems(hiddenMatches, "match") : formatMoreItems(hiddenLines, "line");
|
|
239
|
-
|
|
264
|
+
out.push(`${uiTheme.fg("dim", uiTheme.tree.last)} ${uiTheme.fg("muted", hiddenLabel)}`);
|
|
240
265
|
}
|
|
241
|
-
return
|
|
266
|
+
return out;
|
|
242
267
|
}
|
|
243
268
|
|
|
244
269
|
function grepStatusIcon(uiTheme: Theme): string {
|
|
@@ -382,14 +407,14 @@ export const grepToolRenderer = {
|
|
|
382
407
|
// Header/match display paths are cwd-relative, so resolve them against cwd
|
|
383
408
|
// (falling back to searchPath for legacy results that predate `cwd`); the
|
|
384
409
|
// scoped file's absolute path seeds body lines in single-file searches.
|
|
385
|
-
const
|
|
410
|
+
const styleLine = createSearchLineStyler(
|
|
386
411
|
allLines,
|
|
387
412
|
details?.cwd ?? details?.searchPath,
|
|
388
413
|
details?.searchPath,
|
|
389
414
|
uiTheme,
|
|
390
415
|
details?.displayTargets,
|
|
391
416
|
);
|
|
392
|
-
const matchGroups = groupLineIndicesByBlank(allLines)
|
|
417
|
+
const matchGroups = groupLineIndicesByBlank(allLines);
|
|
393
418
|
|
|
394
419
|
const extraLines: string[] = [];
|
|
395
420
|
if (missingNote) extraLines.push(missingNote);
|
|
@@ -401,7 +426,15 @@ export const grepToolRenderer = {
|
|
|
401
426
|
(options.expanded ? EXPANDED_TEXT_LIMIT : COLLAPSED_TEXT_LIMIT) - extraLines.length,
|
|
402
427
|
0,
|
|
403
428
|
);
|
|
404
|
-
const matchLines = renderBudgetedSearchGroups(
|
|
429
|
+
const matchLines = renderBudgetedSearchGroups(
|
|
430
|
+
matchGroups,
|
|
431
|
+
allLines,
|
|
432
|
+
styleLine,
|
|
433
|
+
budget,
|
|
434
|
+
matchCount,
|
|
435
|
+
uiTheme,
|
|
436
|
+
!options.expanded,
|
|
437
|
+
);
|
|
405
438
|
return [header, ...matchLines, ...extraLines].map(l => truncateToWidth(l, width, Ellipsis.Omit));
|
|
406
439
|
},
|
|
407
440
|
{ paddingX: 1 },
|
package/src/tools/index.ts
CHANGED
|
@@ -27,6 +27,7 @@ import { todoToolRenderer } from "./todo";
|
|
|
27
27
|
import { createVibeToolRenderer } from "./vibe";
|
|
28
28
|
import { webSearchToolRenderer } from "./web-search";
|
|
29
29
|
import { writeToolRenderer } from "./write";
|
|
30
|
+
import { yieldToolRenderer } from "./yield";
|
|
30
31
|
import { setXdevRendererLookup } from "./xdev";
|
|
31
32
|
|
|
32
33
|
export * from "./renderer";
|
|
@@ -67,6 +68,7 @@ export const toolRenderers: Record<string, ToolRenderer> = {
|
|
|
67
68
|
vibe_kill: createVibeToolRenderer("kill"),
|
|
68
69
|
vibe_list: createVibeToolRenderer("list"),
|
|
69
70
|
write: writeToolRenderer,
|
|
71
|
+
yield: yieldToolRenderer,
|
|
70
72
|
};
|
|
71
73
|
|
|
72
74
|
// Wire the xd:// render delegation without the xdev module importing this registry.
|
package/src/tools/lsp.ts
CHANGED
|
@@ -205,38 +205,56 @@ export function renderResult(
|
|
|
205
205
|
if (request?.new_name) requestLines.push(theme.fg("dim", `new name: ${request.new_name}`));
|
|
206
206
|
if (request?.apply !== undefined) requestLines.push(theme.fg("dim", `apply: ${request.apply ? "true" : "false"}`));
|
|
207
207
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
// Determine label, state, bodyLines based on type + current expanded
|
|
213
|
-
let label = "Result";
|
|
214
|
-
let state: "success" | "warning" | "error" = "success";
|
|
215
|
-
let bodyLines: string[] = [];
|
|
216
|
-
|
|
208
|
+
// The body depends only on the (immutable) text, theme and `expanded`; parse
|
|
209
|
+
// and render it once per expanded state instead of on every spinner frame.
|
|
210
|
+
const bodies: Array<LspResultBody | undefined> = [undefined, undefined];
|
|
211
|
+
const renderBody = (expanded: boolean): LspResultBody => {
|
|
217
212
|
if (codeBlockMatch) {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
213
|
+
return {
|
|
214
|
+
label: "Hover",
|
|
215
|
+
state: "success",
|
|
216
|
+
bodyLines: renderHover(codeBlockMatch, text, lines, expanded, theme),
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
if (errorMatch || warningMatch || hasStatusError) {
|
|
222
220
|
const errorCount = errorMatch ? Number.parseInt(errorMatch[1], 10) : 0;
|
|
223
221
|
const warnCount = warningMatch ? Number.parseInt(warningMatch[1], 10) : 0;
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
} else {
|
|
237
|
-
label = "Response";
|
|
238
|
-
bodyLines = renderGeneric(text, lines, expanded, theme);
|
|
222
|
+
return {
|
|
223
|
+
label: "Diagnostics",
|
|
224
|
+
state: errorCount > 0 ? "error" : warnCount > 0 ? "warning" : "success",
|
|
225
|
+
bodyLines: renderDiagnostics(errorMatch, warningMatch, lines, expanded, theme),
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
if (refMatch) {
|
|
229
|
+
return {
|
|
230
|
+
label: "References",
|
|
231
|
+
state: "success",
|
|
232
|
+
bodyLines: renderReferences(refMatch, lines, expanded, theme),
|
|
233
|
+
};
|
|
239
234
|
}
|
|
235
|
+
if (symbolsMatch) {
|
|
236
|
+
return { label: "Symbols", state: "success", bodyLines: renderSymbols(symbolsMatch, lines, expanded, theme) };
|
|
237
|
+
}
|
|
238
|
+
if (result.details?.action === "diagnostics" && text === "OK") {
|
|
239
|
+
return {
|
|
240
|
+
label: "Diagnostics",
|
|
241
|
+
state: "success",
|
|
242
|
+
bodyLines: [`${theme.styledSymbol("tool.lsp", "accent")} ${theme.fg("dim", "OK")}`],
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
return { label: "Response", state: "success", bodyLines: renderGeneric(text, lines, expanded, theme) };
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
return framedToolCard(theme, () => {
|
|
249
|
+
// Read mutable state at render time
|
|
250
|
+
const { expanded, isPartial, spinnerFrame } = options;
|
|
251
|
+
const slot = expanded ? 1 : 0;
|
|
252
|
+
let body = bodies[slot];
|
|
253
|
+
if (!body) {
|
|
254
|
+
body = renderBody(expanded);
|
|
255
|
+
bodies[slot] = body;
|
|
256
|
+
}
|
|
257
|
+
const { label, state, bodyLines } = body;
|
|
240
258
|
|
|
241
259
|
const actionLabel = (request?.action ?? result.details?.action ?? label.toLowerCase()).replace(/_/g, " ");
|
|
242
260
|
const isSuccess = !isPartial && !result.isError;
|
|
@@ -550,36 +568,32 @@ function renderSymbols(symbolsMatch: RegExpMatchArray, lines: string[], expanded
|
|
|
550
568
|
}
|
|
551
569
|
}
|
|
552
570
|
|
|
553
|
-
|
|
571
|
+
// One linear pass each: `isLast[i]` — the next symbol at indent <= mine is
|
|
572
|
+
// not a sibling (monotonic stack, right to left); `prefixes[i]` — per
|
|
573
|
+
// ancestor level, the nearest earlier symbol at that indent decides the rail.
|
|
574
|
+
const isLast = Array.from({ length: symbols.length }, () => false);
|
|
575
|
+
const pending: number[] = [];
|
|
576
|
+
for (let i = symbols.length - 1; i >= 0; i--) {
|
|
554
577
|
const myIndent = symbols[i].indent;
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
let ancestorIdx = -1;
|
|
570
|
-
for (let j = i - 1; j >= 0; j--) {
|
|
571
|
-
if (symbols[j].indent === level - 2) {
|
|
572
|
-
ancestorIdx = j;
|
|
573
|
-
break;
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
|
-
if (ancestorIdx >= 0 && isLastSibling(ancestorIdx)) {
|
|
577
|
-
prefix += " ";
|
|
578
|
-
} else {
|
|
579
|
-
prefix += `${theme.tree.vertical} `;
|
|
578
|
+
while (pending.length > 0 && symbols[pending[pending.length - 1]].indent > myIndent) pending.pop();
|
|
579
|
+
const next = pending.length > 0 ? pending[pending.length - 1] : -1;
|
|
580
|
+
isLast[i] = next < 0 || symbols[next].indent !== myIndent;
|
|
581
|
+
pending.push(i);
|
|
582
|
+
}
|
|
583
|
+
const getPrefixes = (): string[] => {
|
|
584
|
+
const prefixes: string[] = [];
|
|
585
|
+
const lastAtIndent = new Map<number, number>();
|
|
586
|
+
for (let i = 0; i < symbols.length; i++) {
|
|
587
|
+
const myIndent = symbols[i].indent;
|
|
588
|
+
let prefix = " ";
|
|
589
|
+
for (let level = 2; level <= myIndent; level += 2) {
|
|
590
|
+
const ancestorIdx = lastAtIndent.get(level - 2);
|
|
591
|
+
prefix += ancestorIdx !== undefined && isLast[ancestorIdx] ? " " : `${theme.tree.vertical} `;
|
|
580
592
|
}
|
|
593
|
+
prefixes.push(prefix);
|
|
594
|
+
lastAtIndent.set(myIndent, i);
|
|
581
595
|
}
|
|
582
|
-
return
|
|
596
|
+
return prefixes;
|
|
583
597
|
};
|
|
584
598
|
|
|
585
599
|
const topLevelCount = symbols.filter(s => s.indent === 0).length;
|
|
@@ -587,12 +601,13 @@ function renderSymbols(symbolsMatch: RegExpMatchArray, lines: string[], expanded
|
|
|
587
601
|
if (expanded) {
|
|
588
602
|
let output = `${icon} ${theme.fg("dim", `in ${fileName}`)}`;
|
|
589
603
|
|
|
604
|
+
const prefixes = getPrefixes();
|
|
590
605
|
for (let i = 0; i < symbols.length; i++) {
|
|
591
606
|
const sym = symbols[i];
|
|
592
|
-
const prefix =
|
|
593
|
-
const
|
|
594
|
-
const branch =
|
|
595
|
-
const detailPrefix =
|
|
607
|
+
const prefix = prefixes[i];
|
|
608
|
+
const symIsLast = isLast[i];
|
|
609
|
+
const branch = symIsLast ? theme.tree.last : theme.tree.branch;
|
|
610
|
+
const detailPrefix = symIsLast ? " " : `${theme.tree.vertical} `;
|
|
596
611
|
output += `\n${prefix}${theme.fg("dim", branch)} ${theme.fg("accent", sym.icon)} ${theme.fg("accent", sym.name)}`;
|
|
597
612
|
output += `\n${prefix}${theme.fg("dim", detailPrefix)}${theme.fg("muted", `line ${sym.line}`)}`;
|
|
598
613
|
}
|
|
@@ -681,6 +696,13 @@ interface RawDiagnostic {
|
|
|
681
696
|
raw: string;
|
|
682
697
|
}
|
|
683
698
|
|
|
699
|
+
/** A rendered LSP result body for one `expanded` state. */
|
|
700
|
+
interface LspResultBody {
|
|
701
|
+
label: string;
|
|
702
|
+
state: "success" | "warning" | "error";
|
|
703
|
+
bodyLines: string[];
|
|
704
|
+
}
|
|
705
|
+
|
|
684
706
|
type DiagnosticItem = ParsedDiagnostic | RawDiagnostic;
|
|
685
707
|
|
|
686
708
|
function formatDiagnosticMessage(diagnostic: ParsedDiagnostic): string {
|