@linxiraos/pi-tui 1.2.1 → 1.3.1
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 +245 -224
- package/README.md +36 -0
- package/THIRD-PARTY-NOTICES.txt +55 -5
- package/dist/types/app-keybindings.d.ts +18 -8
- package/dist/types/apps/git/diff-pane.d.ts +5 -0
- package/dist/types/apps/git/git-tui.d.ts +16 -2
- package/dist/types/apps/git/state.d.ts +11 -0
- package/dist/types/autocomplete.d.ts +2 -0
- package/dist/types/charts/chart-svg.d.ts +7 -13
- package/dist/types/chat/advisor-message.d.ts +4 -1
- package/dist/types/chat/assistant-message.d.ts +9 -4
- package/dist/types/chat/figure-markdown.d.ts +7 -2
- package/dist/types/chat/messages.d.ts +3 -1
- package/dist/types/chat/read-tool-group.d.ts +2 -2
- package/dist/types/chat/table-chart.d.ts +11 -15
- package/dist/types/chat/tool-execution.d.ts +32 -20
- package/dist/types/chat/user-message.d.ts +3 -2
- package/dist/types/chrome/countdown-timer.d.ts +3 -2
- package/dist/types/chrome/diff.d.ts +2 -3
- package/dist/types/chrome/status-notice.d.ts +2 -0
- package/dist/types/chrome/transcript-container.d.ts +2 -0
- package/dist/types/components/image.d.ts +2 -0
- package/dist/types/components/markdown.d.ts +49 -3
- package/dist/types/components/text.d.ts +2 -0
- package/dist/types/debug-server.d.ts +2 -0
- package/dist/types/keybinding-matchers.d.ts +6 -0
- package/dist/types/multiplexers/tmux.d.ts +1 -0
- package/dist/types/multiplexers/types.d.ts +6 -0
- package/dist/types/native/backend.d.ts +5 -4
- package/dist/types/native/blobs.d.ts +24 -12
- package/dist/types/overlays/agent-hub-projection.d.ts +10 -5
- package/dist/types/overlays/annotation-overlay.d.ts +19 -0
- package/dist/types/overlays/approval-dialog.d.ts +79 -0
- package/dist/types/overlays/ask-dialog.d.ts +11 -6
- package/dist/types/overlays/btw-history-panel.d.ts +2 -6
- package/dist/types/overlays/hook-editor.d.ts +7 -2
- package/dist/types/overlays/hook-input.d.ts +12 -4
- package/dist/types/overlays/job-tree.d.ts +50 -0
- package/dist/types/overlays/jobs-panel.d.ts +25 -11
- package/dist/types/overlays/login-dialog.d.ts +8 -3
- package/dist/types/overlays/mcp-add-wizard.d.ts +9 -1
- package/dist/types/overlays/model-browser.d.ts +6 -1
- package/dist/types/overlays/model-picker.d.ts +2 -0
- package/dist/types/overlays/pause-screen.d.ts +5 -0
- package/dist/types/overlays/plan-review-overlay.d.ts +4 -0
- package/dist/types/overlays/report-panel.d.ts +5 -1
- package/dist/types/overlays/reset-usage-selector.d.ts +7 -5
- package/dist/types/overlays/session-account-selector.d.ts +4 -0
- package/dist/types/overlays/session-info-overlay.d.ts +7 -3
- package/dist/types/overlays/session-selector.d.ts +3 -1
- package/dist/types/overlays/usage-dashboard.d.ts +6 -6
- package/dist/types/prompt/composer.d.ts +23 -20
- package/dist/types/prompt/custom-editor.d.ts +20 -10
- package/dist/types/prompt/image-references.d.ts +22 -0
- package/dist/types/prompt/internal-url-autocomplete.d.ts +1 -0
- package/dist/types/prompt/macos-spelling.d.ts +2 -1
- package/dist/types/prompt/prose-gate.d.ts +3 -0
- package/dist/types/prompt/queued-messages.d.ts +1 -1
- package/dist/types/prompt/typing-word.d.ts +25 -0
- package/dist/types/prompt/welcome.d.ts +26 -6
- package/dist/types/render/hanging-wrap.d.ts +7 -0
- package/dist/types/render/hyperlink.d.ts +2 -0
- package/dist/types/render/render-utils.d.ts +9 -1
- package/dist/types/render/utils.d.ts +1 -1
- package/dist/types/setup/scenes/splash.d.ts +6 -5
- package/dist/types/status-line/context-usage.d.ts +4 -1
- package/dist/types/status-line/host.d.ts +6 -4
- package/dist/types/status-line/startup.d.ts +6 -6
- package/dist/types/status-line/types.d.ts +3 -0
- package/dist/types/stdin-buffer.d.ts +7 -0
- package/dist/types/terminal-capabilities.d.ts +16 -1
- package/dist/types/terminal-multiplexer.d.ts +7 -0
- package/dist/types/terminal.d.ts +8 -4
- package/dist/types/theme/symbols.d.ts +1 -1
- package/dist/types/theme/theme.d.ts +11 -0
- package/dist/types/thinking.d.ts +5 -0
- package/dist/types/tmux.d.ts +39 -4
- package/dist/types/tools/ast-edit.d.ts +2 -2
- package/dist/types/tools/ast-grep.d.ts +2 -2
- package/dist/types/tools/bash.d.ts +6 -1
- package/dist/types/tools/edit.d.ts +2 -0
- package/dist/types/tools/eval.d.ts +53 -4
- package/dist/types/tools/find.d.ts +14 -4
- package/dist/types/tools/glob.d.ts +3 -3
- package/dist/types/tools/grep.d.ts +2 -2
- package/dist/types/tools/grouped-file-output.d.ts +9 -4
- package/dist/types/tools/native-view.d.ts +1 -1
- package/dist/types/tools/read.d.ts +32 -10
- package/dist/types/tools/renderer.d.ts +2 -0
- package/dist/types/tools/task.d.ts +31 -0
- package/dist/types/tools/web-search-types.d.ts +1 -0
- package/dist/types/tools/write.d.ts +2 -0
- package/dist/types/tui.d.ts +20 -3
- package/package.json +10 -10
- package/src/app-keybindings.d.ts +18 -8
- package/src/app-keybindings.ts +20 -8
- package/src/apps/autoresearch-dashboard.ts +444 -27
- package/src/apps/debug/raw-sse.ts +2 -2
- package/src/apps/git/diff-pane.ts +39 -41
- package/src/apps/git/git-tui.ts +80 -14
- package/src/apps/git/sidebar.ts +10 -1
- package/src/apps/git/state.ts +12 -0
- package/src/autocomplete.d.ts +2 -0
- package/src/autocomplete.ts +21 -19
- package/src/bracketed-paste.ts +9 -2
- package/src/charts/chart-svg.d.ts +7 -13
- package/src/charts/chart-svg.ts +19 -22
- package/src/charts/table-data.ts +17 -6
- package/src/chat/advisor-message.d.ts +4 -1
- package/src/chat/advisor-message.ts +77 -24
- package/src/chat/assistant-message.d.ts +9 -4
- package/src/chat/assistant-message.ts +127 -31
- package/src/chat/bash-execution.ts +7 -2
- package/src/chat/figure-markdown.d.ts +7 -2
- package/src/chat/figure-markdown.ts +10 -3
- package/src/chat/image-loading.ts +9 -6
- package/src/chat/messages.d.ts +3 -1
- package/src/chat/messages.ts +4 -1
- package/src/chat/read-tool-group.d.ts +2 -2
- package/src/chat/read-tool-group.ts +97 -42
- package/src/chat/svg-figure.ts +3 -1
- package/src/chat/svg-source.ts +6 -3
- package/src/chat/table-chart.d.ts +11 -15
- package/src/chat/table-chart.ts +24 -57
- package/src/chat/tool-execution.d.ts +32 -20
- package/src/chat/tool-execution.ts +103 -36
- package/src/chat/user-message.d.ts +3 -2
- package/src/chat/user-message.ts +54 -34
- package/src/chrome/countdown-timer.ts +3 -3
- package/src/chrome/diff.d.ts +2 -3
- package/src/chrome/diff.ts +4 -9
- package/src/chrome/status-notice.ts +5 -0
- package/src/chrome/transcript-container.d.ts +2 -0
- package/src/chrome/transcript-container.ts +212 -28
- package/src/components/editor.ts +12 -12
- package/src/components/image.d.ts +2 -0
- package/src/components/image.ts +54 -20
- package/src/components/markdown.d.ts +49 -3
- package/src/components/markdown.ts +482 -12
- package/src/components/settings-list.ts +2 -4
- package/src/components/text.d.ts +2 -0
- package/src/components/text.ts +11 -1
- package/src/debug-server.d.ts +2 -0
- package/src/debug-server.ts +51 -69
- package/src/hotkeys-markdown.ts +2 -0
- package/src/keybinding-matchers.d.ts +6 -0
- package/src/keybinding-matchers.ts +19 -0
- package/src/multiplexers/tmux.d.ts +1 -0
- package/src/multiplexers/tmux.ts +3 -0
- package/src/multiplexers/types.d.ts +6 -0
- package/src/multiplexers/types.ts +6 -0
- package/src/native/backend.d.ts +5 -4
- package/src/native/backend.ts +13 -5
- package/src/native/blobs.d.ts +24 -12
- package/src/native/blobs.ts +56 -7
- package/src/native/reconcile.ts +43 -21
- package/src/overlays/advisor-config.ts +259 -8
- package/src/overlays/agent-hub-projection.d.ts +10 -5
- package/src/overlays/agent-hub-projection.ts +63 -12
- package/src/overlays/annotation-overlay.ts +422 -168
- package/src/overlays/approval-dialog.ts +389 -0
- package/src/overlays/ask-dialog.ts +244 -105
- package/src/overlays/btw-history-panel.ts +208 -147
- package/src/overlays/extensions/extension-list.ts +4 -6
- package/src/overlays/hook-editor.ts +90 -32
- package/src/overlays/hook-input.ts +105 -19
- package/src/overlays/job-tree.ts +177 -0
- package/src/overlays/jobs-panel.ts +222 -142
- package/src/overlays/login-dialog.ts +201 -91
- package/src/overlays/mcp-add-wizard.ts +562 -279
- package/src/overlays/model-browser.ts +32 -7
- package/src/overlays/model-hub.ts +4 -10
- package/src/overlays/model-picker.ts +58 -21
- package/src/overlays/pause-screen.ts +81 -23
- package/src/overlays/plan-review-overlay.ts +16 -0
- package/src/overlays/plugin-selector.ts +17 -1
- package/src/overlays/queue-mode-selector.ts +10 -0
- package/src/overlays/report-panel.ts +6 -2
- package/src/overlays/reset-usage-selector.ts +130 -21
- package/src/overlays/session-account-selector.ts +36 -0
- package/src/overlays/session-info-overlay.ts +199 -76
- package/src/overlays/session-selector.ts +15 -10
- package/src/overlays/settings-selector.ts +3 -0
- package/src/overlays/show-images-selector.ts +1 -0
- package/src/overlays/snapcompact-shape-preview.ts +13 -6
- package/src/overlays/theme-selector.ts +6 -1
- package/src/overlays/thinking-selector.ts +8 -4
- package/src/overlays/tree-selector.ts +117 -2
- package/src/overlays/usage-dashboard.ts +498 -144
- package/src/prompt/composer-cache.ts +4 -1
- package/src/prompt/composer.ts +200 -74
- package/src/prompt/custom-editor.ts +168 -65
- package/src/prompt/image-references.d.ts +22 -0
- package/src/prompt/image-references.ts +79 -2
- package/src/prompt/internal-url-autocomplete.ts +1 -0
- package/src/prompt/macos-spelling.ts +28 -5
- package/src/prompt/prose-gate.ts +3 -0
- package/src/prompt/queued-messages.ts +9 -2
- package/src/prompt/tips.txt +1 -2
- package/src/prompt/typing-word.ts +59 -0
- package/src/prompt/welcome.ts +97 -45
- package/src/render/code-cell.ts +18 -5
- package/src/render/hanging-wrap.d.ts +7 -0
- package/src/render/hanging-wrap.ts +58 -0
- package/src/render/hyperlink.d.ts +2 -0
- package/src/render/hyperlink.ts +5 -0
- package/src/render/render-utils.d.ts +9 -1
- package/src/render/render-utils.ts +16 -2
- package/src/render/utils.d.ts +1 -1
- package/src/render/utils.ts +1 -1
- package/src/render/width-aware-text.ts +6 -5
- package/src/setup/scenes/splash.ts +22 -8
- package/src/status-line/component.ts +79 -11
- package/src/status-line/context-usage.ts +176 -73
- package/src/status-line/footer.ts +18 -35
- package/src/status-line/host.ts +6 -1
- package/src/status-line/segments.ts +16 -5
- package/src/status-line/startup.ts +6 -6
- package/src/status-line/types.ts +1 -0
- package/src/stdin-buffer.d.ts +7 -0
- package/src/stdin-buffer.ts +300 -62
- package/src/terminal-capabilities.d.ts +16 -1
- package/src/terminal-capabilities.ts +66 -28
- package/src/terminal-multiplexer.d.ts +7 -0
- package/src/terminal-multiplexer.ts +14 -0
- package/src/terminal.d.ts +8 -4
- package/src/terminal.ts +190 -63
- package/src/theme/symbols.d.ts +1 -1
- package/src/theme/symbols.ts +1 -0
- package/src/theme/theme-class.ts +1 -0
- package/src/theme/theme.d.ts +11 -0
- package/src/theme/theme.ts +46 -14
- package/src/thinking.d.ts +5 -0
- package/src/thinking.ts +9 -1
- package/src/tmux.d.ts +39 -4
- package/src/tmux.ts +110 -16
- package/src/tools/ask.ts +20 -9
- package/src/tools/ast-edit.d.ts +2 -2
- package/src/tools/ast-edit.ts +30 -7
- package/src/tools/ast-grep.d.ts +2 -2
- package/src/tools/ast-grep.ts +39 -35
- package/src/tools/bash.d.ts +6 -1
- package/src/tools/bash.ts +15 -9
- package/src/tools/debug.ts +2 -1
- package/src/tools/default-renderer.ts +3 -2
- package/src/tools/edit.d.ts +2 -0
- package/src/tools/edit.ts +100 -21
- package/src/tools/eval.d.ts +53 -4
- package/src/tools/eval.ts +173 -137
- package/src/tools/find.d.ts +14 -4
- package/src/tools/find.ts +71 -18
- package/src/tools/glob.d.ts +3 -3
- package/src/tools/glob.ts +51 -17
- package/src/tools/grep.d.ts +2 -2
- package/src/tools/grep.ts +99 -22
- package/src/tools/grouped-file-output.d.ts +9 -4
- package/src/tools/grouped-file-output.ts +21 -6
- package/src/tools/lsp.ts +30 -18
- package/src/tools/mcp.ts +2 -2
- package/src/tools/native-view.d.ts +1 -1
- package/src/tools/native-view.ts +2 -2
- package/src/tools/read.d.ts +32 -10
- package/src/tools/read.ts +89 -43
- package/src/tools/renderer.d.ts +2 -0
- package/src/tools/renderer.ts +2 -0
- package/src/tools/task.d.ts +31 -0
- package/src/tools/task.ts +40 -7
- package/src/tools/todo.ts +3 -2
- package/src/tools/wait.ts +16 -30
- package/src/tools/web-search-types.d.ts +1 -0
- package/src/tools/web-search-types.ts +1 -0
- package/src/tools/web-search.ts +2 -1
- package/src/tools/write.d.ts +2 -0
- package/src/tools/write.ts +36 -12
- package/src/tools/yield.ts +2 -2
- package/src/tui.d.ts +20 -3
- package/src/tui.ts +160 -43
- package/src/utils.ts +5 -3
- package/dist/types/tools/eval-format/code-mode-declarations.d.ts +0 -5
- package/src/tools/eval-format/code-mode-declarations.ts +0 -61
package/src/tools/ast-grep.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isRecord, sanitizeText } from "@linxiraos/pi-utils";
|
|
1
2
|
import type { Component } from "../tui";
|
|
2
3
|
import { Text } from "../components/text";
|
|
3
4
|
import type { Theme } from "../theme/theme";
|
|
@@ -13,13 +14,11 @@ import {
|
|
|
13
14
|
toPathList,
|
|
14
15
|
} from "../render/render-utils";
|
|
15
16
|
import { classifyGroupedLines, describeGroupedOutput, groupLineIndicesByBlank } from "./grouped-file-output";
|
|
16
|
-
import { getLanguageFromPath } from "../lang-from-path";
|
|
17
17
|
import { code, compact, node, span } from "../native/describe";
|
|
18
18
|
import type { NativeChild } from "../native/node";
|
|
19
19
|
import { errorText, noteText, resultText, toolHead } from "./native-view";
|
|
20
20
|
import type { OutputMeta } from "./output-meta";
|
|
21
|
-
import {
|
|
22
|
-
import type { RenderResultOptions, ToolRenderer, NativeToolView } from "./renderer";
|
|
21
|
+
import type { NativeToolView, RenderResultContextOptions, RenderResultOptions, ToolRenderer } from "./renderer";
|
|
23
22
|
|
|
24
23
|
/** Display metadata returned by ast-grep. */
|
|
25
24
|
export interface AstGrepToolDetails {
|
|
@@ -61,8 +60,7 @@ export const astGrepToolRenderer = {
|
|
|
61
60
|
renderCall(args: AstGrepRenderArgs, _options: RenderResultOptions, uiTheme: Theme): Component {
|
|
62
61
|
const meta: string[] = [];
|
|
63
62
|
const scopePaths = toPathList(args.path ?? args.paths);
|
|
64
|
-
if (scopePaths.length)
|
|
65
|
-
meta.push(tuiTextFmt("gpInLabelFmt", `in ${scopePaths.join(", ")}`, scopePaths.join(", ")));
|
|
63
|
+
if (scopePaths.length) meta.push(`in ${scopePaths.join(", ")}`);
|
|
66
64
|
if (args.skip !== undefined && args.skip > 0) meta.push(`skip:${args.skip}`);
|
|
67
65
|
|
|
68
66
|
const description = args.pat ?? "?";
|
|
@@ -72,15 +70,14 @@ export const astGrepToolRenderer = {
|
|
|
72
70
|
|
|
73
71
|
renderResult(
|
|
74
72
|
result: { content: Array<{ type: string; text?: string }>; details?: AstGrepToolDetails; isError?: boolean },
|
|
75
|
-
options:
|
|
73
|
+
options: RenderResultContextOptions,
|
|
76
74
|
uiTheme: Theme,
|
|
77
75
|
args?: AstGrepRenderArgs,
|
|
78
76
|
): Component {
|
|
79
77
|
const details = result.details;
|
|
80
78
|
|
|
81
79
|
if (result.isError) {
|
|
82
|
-
const errorText =
|
|
83
|
-
result.content?.find(c => c.type === "text")?.text || tuiText("glUnknownError", "Unknown error");
|
|
80
|
+
const errorText = result.content?.find(c => c.type === "text")?.text || "Unknown error";
|
|
84
81
|
return new Text(formatErrorMessage(errorText, uiTheme), 0, 0);
|
|
85
82
|
}
|
|
86
83
|
|
|
@@ -91,33 +88,23 @@ export const astGrepToolRenderer = {
|
|
|
91
88
|
|
|
92
89
|
if (matchCount === 0) {
|
|
93
90
|
const description = args?.pat;
|
|
94
|
-
const meta = [
|
|
95
|
-
if (details?.scopePath) meta.push(
|
|
96
|
-
if (filesSearched > 0) meta.push(
|
|
91
|
+
const meta = ["0 matches"];
|
|
92
|
+
if (details?.scopePath) meta.push(`in ${details.scopePath}`);
|
|
93
|
+
if (filesSearched > 0) meta.push(`searched ${filesSearched}`);
|
|
97
94
|
const header = renderStatusLine({ icon: "warning", title: "AST Grep", description, meta }, uiTheme);
|
|
98
|
-
const lines = [header, formatEmptyMessage(
|
|
95
|
+
const lines = [header, formatEmptyMessage("No matches found", uiTheme)];
|
|
99
96
|
if (details?.parseErrors?.length) {
|
|
100
|
-
lines.push(
|
|
101
|
-
uiTheme.fg(
|
|
102
|
-
"warning",
|
|
103
|
-
tuiText("agrpMisScoped", "Query may be mis-scoped; narrow `path` before concluding absence"),
|
|
104
|
-
),
|
|
105
|
-
);
|
|
97
|
+
lines.push(uiTheme.fg("warning", "Query may be mis-scoped; narrow `path` before concluding absence"));
|
|
106
98
|
appendParseErrorsBulletList(lines, details.parseErrors, uiTheme, details.parseErrorsTotal);
|
|
107
99
|
}
|
|
108
100
|
return new Text(lines.join("\n"), 0, 0);
|
|
109
101
|
}
|
|
110
102
|
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
fileCount === 1 ? tuiText("agrpFileOne", "1 file") : tuiTextFmt("agrpFileManyFmt", "%d files", fileCount);
|
|
117
|
-
const meta = [matchMeta, fileMeta];
|
|
118
|
-
if (details?.scopePath) meta.push(tuiTextFmt("gpInLabelFmt", `in ${details.scopePath}`, details.scopePath));
|
|
119
|
-
meta.push(tuiTextFmt("agrpSearchedFmt", `searched ${filesSearched}`, filesSearched));
|
|
120
|
-
if (limitReached) meta.push(uiTheme.fg("warning", tuiText("tLimitReached", "limit reached")));
|
|
103
|
+
const summaryParts = [formatCount("match", matchCount), formatCount("file", fileCount)];
|
|
104
|
+
const meta = [...summaryParts];
|
|
105
|
+
if (details?.scopePath) meta.push(`in ${details.scopePath}`);
|
|
106
|
+
meta.push(`searched ${filesSearched}`);
|
|
107
|
+
if (limitReached) meta.push(uiTheme.fg("warning", "limit reached"));
|
|
121
108
|
const description = args?.pat;
|
|
122
109
|
const header = renderStatusLine(
|
|
123
110
|
{
|
|
@@ -136,6 +123,20 @@ export const astGrepToolRenderer = {
|
|
|
136
123
|
// Resolve hyperlinks over the whole output so nested directory headers
|
|
137
124
|
// reconstruct across the blank-line groups the tree list collapses by.
|
|
138
125
|
const contexts = classifyGroupedLines(allLines, details?.cwd ?? details?.searchPath, details?.searchPath);
|
|
126
|
+
// The displayed lines are sanitized, but OSC 8 destinations must retain the
|
|
127
|
+
// original path bytes. Only substitute targets for headers already linked
|
|
128
|
+
// by the sanitized output; never let raw text create additional links.
|
|
129
|
+
const rawDetails = options.renderContext?.rawDetails;
|
|
130
|
+
const rawContent =
|
|
131
|
+
isRecord(rawDetails) && typeof rawDetails.displayContent === "string" ? rawDetails.displayContent : undefined;
|
|
132
|
+
const rawLines = rawContent?.split("\n");
|
|
133
|
+
const rawCwd = isRecord(rawDetails) && typeof rawDetails.cwd === "string" ? rawDetails.cwd : undefined;
|
|
134
|
+
const rawSearchPath =
|
|
135
|
+
isRecord(rawDetails) && typeof rawDetails.searchPath === "string" ? rawDetails.searchPath : undefined;
|
|
136
|
+
const rawContexts =
|
|
137
|
+
rawContent !== undefined && rawLines?.length === allLines.length && sanitizeText(rawContent) === textContent
|
|
138
|
+
? classifyGroupedLines(rawLines, rawCwd ?? rawSearchPath, rawSearchPath)
|
|
139
|
+
: undefined;
|
|
139
140
|
// Style lazily: the collapsed tree list only renders the first few groups,
|
|
140
141
|
// so a large result never styles/hyperlinks rows nobody sees.
|
|
141
142
|
const styledLines: (string | undefined)[] = Array.from({ length: allLines.length }, () => undefined);
|
|
@@ -144,13 +145,18 @@ export const astGrepToolRenderer = {
|
|
|
144
145
|
if (cached !== undefined) return cached;
|
|
145
146
|
const line = allLines[index]!;
|
|
146
147
|
const ctx = contexts[index]!;
|
|
148
|
+
const rawContext = rawContexts?.[index];
|
|
149
|
+
const target =
|
|
150
|
+
ctx.headerPath && rawContext?.kind === ctx.kind && rawContext.depth === ctx.depth
|
|
151
|
+
? (rawContext.headerPath ?? ctx.headerPath)
|
|
152
|
+
: ctx.headerPath;
|
|
147
153
|
let styled: string;
|
|
148
154
|
if (ctx.kind === "dir") {
|
|
149
155
|
const accent = uiTheme.fg("accent", line);
|
|
150
|
-
styled =
|
|
156
|
+
styled = target ? fileHyperlink(target, accent) : accent;
|
|
151
157
|
} else if (ctx.kind === "file") {
|
|
152
158
|
const tinted = uiTheme.fg(ctx.depth === 1 ? "accent" : "dim", line);
|
|
153
|
-
styled =
|
|
159
|
+
styled = target ? fileHyperlink(target, tinted) : tinted;
|
|
154
160
|
} else {
|
|
155
161
|
styled = uiTheme.fg(line.startsWith(" meta:") ? "dim" : "toolOutput", line);
|
|
156
162
|
}
|
|
@@ -174,9 +180,7 @@ export const astGrepToolRenderer = {
|
|
|
174
180
|
|
|
175
181
|
const extraLines: string[] = [];
|
|
176
182
|
if (limitReached) {
|
|
177
|
-
extraLines.push(
|
|
178
|
-
uiTheme.fg("warning", tuiText("agrpLimitNarrowPath", "limit reached; narrow path or increase limit")),
|
|
179
|
-
);
|
|
183
|
+
extraLines.push(uiTheme.fg("warning", "limit reached; narrow path or use skip"));
|
|
180
184
|
}
|
|
181
185
|
if (details?.parseErrors?.length) {
|
|
182
186
|
extraLines.push(
|
|
@@ -274,8 +278,8 @@ export const astGrepToolRenderer = {
|
|
|
274
278
|
return {
|
|
275
279
|
head,
|
|
276
280
|
body: compact<NativeChild>([
|
|
277
|
-
...describeGroupedOutput(kept
|
|
278
|
-
limitReached && noteText("limit reached; narrow path or
|
|
281
|
+
...describeGroupedOutput(kept),
|
|
282
|
+
limitReached && noteText("limit reached; narrow path or use skip", "warning"),
|
|
279
283
|
parseNote,
|
|
280
284
|
]),
|
|
281
285
|
preview: { lines: COLLAPSED_MATCH_LIMIT },
|
package/src/tools/bash.d.ts
CHANGED
|
@@ -18,6 +18,8 @@ export interface BashToolDetails {
|
|
|
18
18
|
requestedTimeoutSeconds?: number;
|
|
19
19
|
timeoutDisabled?: boolean;
|
|
20
20
|
wallTimeMs?: number;
|
|
21
|
+
/** False when no wall-time footer was emitted; absent on older results. */
|
|
22
|
+
wallTimeNoticeEmitted?: boolean;
|
|
21
23
|
/** Exit code of a command that ran to completion but failed (non-zero). */
|
|
22
24
|
exitCode?: number;
|
|
23
25
|
/** True when the command was killed by its timeout deadline (not a failure). */
|
|
@@ -31,12 +33,15 @@ export interface BashToolDetails {
|
|
|
31
33
|
timedOut: boolean;
|
|
32
34
|
pid?: number;
|
|
33
35
|
};
|
|
36
|
+
/** Background job behind the call; `cancelled`: killed (`proc://<id>/kill`) before it finished. */
|
|
34
37
|
async?: {
|
|
35
|
-
state: "running" | "completed" | "failed";
|
|
38
|
+
state: "running" | "completed" | "failed" | "cancelled";
|
|
36
39
|
jobId: string;
|
|
37
40
|
type: "bash";
|
|
38
41
|
};
|
|
39
42
|
}
|
|
43
|
+
/** Minimum duration for a model-facing wall-time notice; shorter runs keep timing only in details. */
|
|
44
|
+
export declare const WALL_TIME_NOTICE_MIN_MS = 1000;
|
|
40
45
|
/** Formats the model-facing command duration notice. */
|
|
41
46
|
export declare function formatWallTimeNotice(wallTimeMs: number): string;
|
|
42
47
|
/** Formats the model-facing command exit status notice. */
|
package/src/tools/bash.ts
CHANGED
|
@@ -62,6 +62,8 @@ export interface BashToolDetails {
|
|
|
62
62
|
requestedTimeoutSeconds?: number;
|
|
63
63
|
timeoutDisabled?: boolean;
|
|
64
64
|
wallTimeMs?: number;
|
|
65
|
+
/** False when no wall-time footer was emitted; absent on older results. */
|
|
66
|
+
wallTimeNoticeEmitted?: boolean;
|
|
65
67
|
/** Exit code of a command that ran to completion but failed (non-zero). */
|
|
66
68
|
exitCode?: number;
|
|
67
69
|
/** True when the command was killed by its timeout deadline (not a failure). */
|
|
@@ -75,8 +77,9 @@ export interface BashToolDetails {
|
|
|
75
77
|
timedOut: boolean;
|
|
76
78
|
pid?: number;
|
|
77
79
|
};
|
|
80
|
+
/** Background job behind the call; `cancelled`: killed (`proc://<id>/kill`) before it finished. */
|
|
78
81
|
async?: {
|
|
79
|
-
state: "running" | "completed" | "failed";
|
|
82
|
+
state: "running" | "completed" | "failed" | "cancelled";
|
|
80
83
|
jobId: string;
|
|
81
84
|
type: "bash";
|
|
82
85
|
};
|
|
@@ -208,6 +211,9 @@ function formatWallTimeSeconds(wallTimeMs: number): string {
|
|
|
208
211
|
return (wallTimeMs / 1000).toFixed(2);
|
|
209
212
|
}
|
|
210
213
|
|
|
214
|
+
/** Minimum duration for a model-facing wall-time notice; shorter runs keep timing only in details. */
|
|
215
|
+
export const WALL_TIME_NOTICE_MIN_MS = 1000;
|
|
216
|
+
|
|
211
217
|
/** Formats the model-facing command duration notice. */
|
|
212
218
|
export function formatWallTimeNotice(wallTimeMs: number): string {
|
|
213
219
|
return `Wall time: ${formatWallTimeSeconds(wallTimeMs)} seconds`;
|
|
@@ -318,7 +324,7 @@ function stripBashNotices(
|
|
|
318
324
|
? strippedOutput
|
|
319
325
|
: stripTrailingNotice(strippedOutput, formatExitCodeNotice(details.exitCode));
|
|
320
326
|
const withoutWall =
|
|
321
|
-
details?.wallTimeMs === undefined
|
|
327
|
+
details?.wallTimeMs === undefined || details.wallTimeNoticeEmitted === false
|
|
322
328
|
? withoutExit
|
|
323
329
|
: stripTrailingNotice(withoutExit, formatWallTimeNotice(details.wallTimeMs));
|
|
324
330
|
return stripRawOutputArtifactNotice(withoutWall);
|
|
@@ -426,9 +432,9 @@ function shellRunState(
|
|
|
426
432
|
cancelled: boolean,
|
|
427
433
|
): RunState {
|
|
428
434
|
if (cancelled) return "cancelled";
|
|
435
|
+
if (details?.async?.state === "running") return "background";
|
|
429
436
|
if (isPartial) return "running";
|
|
430
437
|
if (details?.timedOut === true) return "timed-out";
|
|
431
|
-
if (details?.async?.state === "running") return "background";
|
|
432
438
|
if (isError || (details?.exitCode !== undefined && details.exitCode !== 0)) return "failed";
|
|
433
439
|
return "done";
|
|
434
440
|
}
|
|
@@ -677,13 +683,13 @@ export function createShellRenderer<TArgs>(config: ShellRendererConfig<TArgs>) {
|
|
|
677
683
|
const command = formatBashCommandText(toBashRenderArgs(args, config));
|
|
678
684
|
const previewLines = renderContext?.previewLines ?? BASH_DEFAULT_PREVIEW_LINES;
|
|
679
685
|
const state = shellRunState(details, isError, isPartial, options.cancelled === true);
|
|
680
|
-
//
|
|
681
|
-
|
|
682
|
-
|
|
686
|
+
// Live (a running call, or a parked job still streaming): the call's clock. Settled: the
|
|
687
|
+
// measured wall time wins. A frozen background snapshot's run time is unknown here.
|
|
688
|
+
const elapsedMs = isPartial
|
|
689
|
+
? options.elapsedMs
|
|
690
|
+
: state === "background"
|
|
683
691
|
? undefined
|
|
684
|
-
:
|
|
685
|
-
? options.elapsedMs
|
|
686
|
-
: (details?.wallTimeMs ?? options.elapsedMs);
|
|
692
|
+
: (details?.wallTimeMs ?? options.elapsedMs);
|
|
687
693
|
const meta = details?.meta;
|
|
688
694
|
const box = runBox(
|
|
689
695
|
[
|
package/src/tools/debug.ts
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
replaceTabs,
|
|
11
11
|
TRUNCATE_LENGTHS,
|
|
12
12
|
truncateToWidth,
|
|
13
|
+
clipCollapsed,
|
|
13
14
|
} from "../render/render-utils";
|
|
14
15
|
import type { NativeToolView, RenderResultOptions, ToolRenderer, ToolRenderResult } from "./renderer";
|
|
15
16
|
import { ansi, kv } from "../native/describe";
|
|
@@ -210,7 +211,7 @@ export const debugToolRenderer = {
|
|
|
210
211
|
const previewLimit = options.expanded ? PREVIEW_LIMITS.EXPANDED_LINES : PREVIEW_LIMITS.COLLAPSED_LINES;
|
|
211
212
|
const displayedLines = rawLines
|
|
212
213
|
.slice(0, previewLimit)
|
|
213
|
-
.map(line =>
|
|
214
|
+
.map(line => clipCollapsed(line, TRUNCATE_LENGTHS.LINE, options.expanded));
|
|
214
215
|
const remaining = rawLines.length - displayedLines.length;
|
|
215
216
|
if (remaining > 0) {
|
|
216
217
|
displayedLines.push(
|
|
@@ -7,7 +7,7 @@ import type { Theme } from "../theme/theme";
|
|
|
7
7
|
import { formatOutputPaneLines, styleToolOutputLine } from "../render/output-pane";
|
|
8
8
|
import { renderStatusLine, type StatusLineOptions } from "../render/status-line";
|
|
9
9
|
import { plainToolCard, type ToolCardPhase } from "../render/tool-card";
|
|
10
|
-
import {
|
|
10
|
+
import { clipCollapsed } from "../render/render-utils";
|
|
11
11
|
import {
|
|
12
12
|
describeJsonTree,
|
|
13
13
|
formatArgsInline,
|
|
@@ -133,7 +133,8 @@ function buildDefaultToolBody(input: DefaultToolRenderInput, uiTheme: Theme, con
|
|
|
133
133
|
expanded: options.expanded,
|
|
134
134
|
collapsedMaxLines: 4,
|
|
135
135
|
expandedMaxLines: 12,
|
|
136
|
-
styleLine: line =>
|
|
136
|
+
styleLine: line =>
|
|
137
|
+
clipCollapsed(styleToolOutputLine(line, uiTheme), contentWidth, options.expanded),
|
|
137
138
|
showExpandHintWhenUncapped: true,
|
|
138
139
|
},
|
|
139
140
|
uiTheme,
|
package/src/tools/edit.d.ts
CHANGED
|
@@ -133,6 +133,8 @@ export interface EditRenderContext {
|
|
|
133
133
|
renderDiff?: (diffText: string, options?: {
|
|
134
134
|
filePath?: string;
|
|
135
135
|
}) => string;
|
|
136
|
+
/** Unsanitized result metadata, used only for filesystem link targets. */
|
|
137
|
+
rawDetails?: unknown;
|
|
136
138
|
}
|
|
137
139
|
/** Render replacement text until a computed diff preview is available. */
|
|
138
140
|
export declare function renderStreamingFallback(editMode: EditMode, args: unknown, theme: Theme): string;
|
package/src/tools/edit.ts
CHANGED
|
@@ -198,6 +198,8 @@ export interface EditRenderContext {
|
|
|
198
198
|
editStreamingFallback?: string;
|
|
199
199
|
/** Function to render diff text with syntax highlighting */
|
|
200
200
|
renderDiff?: (diffText: string, options?: { filePath?: string }) => string;
|
|
201
|
+
/** Unsanitized result metadata, used only for filesystem link targets. */
|
|
202
|
+
rawDetails?: unknown;
|
|
201
203
|
}
|
|
202
204
|
|
|
203
205
|
/**
|
|
@@ -297,7 +299,7 @@ function truncateEditTitlePath(displayPath: string, maxWidth: number | undefined
|
|
|
297
299
|
}
|
|
298
300
|
|
|
299
301
|
function formatEditTitlePath(pathValue: string, maxWidth?: number): string {
|
|
300
|
-
return truncateEditTitlePath(replaceTabs(shortenPath(pathValue)), maxWidth);
|
|
302
|
+
return truncateEditTitlePath(replaceTabs(shortenPath(sanitizeText(pathValue))), maxWidth);
|
|
301
303
|
}
|
|
302
304
|
|
|
303
305
|
function formatEditPathDisplay(
|
|
@@ -357,6 +359,7 @@ function renderEditHeader(
|
|
|
357
359
|
rename?: string;
|
|
358
360
|
firstChangedLine?: number;
|
|
359
361
|
linkPath?: string;
|
|
362
|
+
renameLinkPath?: string;
|
|
360
363
|
statsSuffix?: string;
|
|
361
364
|
extraSuffix?: string;
|
|
362
365
|
title?: string;
|
|
@@ -367,6 +370,7 @@ function renderEditHeader(
|
|
|
367
370
|
rename: options.rename,
|
|
368
371
|
firstChangedLine: options.firstChangedLine,
|
|
369
372
|
linkPath: options.linkPath,
|
|
373
|
+
renameLinkPath: options.renameLinkPath,
|
|
370
374
|
};
|
|
371
375
|
const formatted = formatEditDescription(options.rawPath, uiTheme, descriptionOptions);
|
|
372
376
|
const suffix = `${options.statsSuffix ?? ""}${options.extraSuffix ?? ""}`;
|
|
@@ -402,7 +406,14 @@ function renderEditHeader(
|
|
|
402
406
|
*/
|
|
403
407
|
function renderInlineEditRow(
|
|
404
408
|
uiTheme: Theme,
|
|
405
|
-
opts: {
|
|
409
|
+
opts: {
|
|
410
|
+
op?: Operation;
|
|
411
|
+
rename?: string;
|
|
412
|
+
rawPath: string;
|
|
413
|
+
linkPath?: string;
|
|
414
|
+
renameLinkPath?: string;
|
|
415
|
+
pending: boolean;
|
|
416
|
+
},
|
|
406
417
|
): Component {
|
|
407
418
|
const isDelete = opts.op === "delete";
|
|
408
419
|
return new WidthAwareText(
|
|
@@ -417,6 +428,7 @@ function renderInlineEditRow(
|
|
|
417
428
|
rawPath: opts.rawPath,
|
|
418
429
|
rename: opts.rename,
|
|
419
430
|
linkPath: opts.linkPath,
|
|
431
|
+
renameLinkPath: opts.renameLinkPath,
|
|
420
432
|
}),
|
|
421
433
|
0,
|
|
422
434
|
0,
|
|
@@ -481,6 +493,21 @@ interface StreamingDiffTail {
|
|
|
481
493
|
hidden: boolean;
|
|
482
494
|
}
|
|
483
495
|
|
|
496
|
+
/**
|
|
497
|
+
* Visual rows a raw diff line occupies once `renderDiff` gives it a code-frame
|
|
498
|
+
* gutter and `wrapEditRendererLine` hangs its continuations beneath it.
|
|
499
|
+
* Canonical `+12|` rows render a padded `max(3, digits) + 1` gutter plus `│`;
|
|
500
|
+
* rows already carrying a `│` rail keep their own prefix. Content wraps at the
|
|
501
|
+
* width left after that gutter; unnumbered rows wrap whole.
|
|
502
|
+
*/
|
|
503
|
+
function streamingDiffLineRows(rawLine: string, innerWidth: number): number {
|
|
504
|
+
const numbered = /^([+\-\s]\s*(\d+)[|│])(.*)$/s.exec(rawLine) ?? /^([+\-\s]\s*(\d+)\s)(.*)$/s.exec(rawLine);
|
|
505
|
+
if (!numbered) return Math.max(1, wrapTextWithAnsi(replaceTabs(rawLine), innerWidth).length);
|
|
506
|
+
const gutterWidth = Math.max(visibleWidth(numbered[1]!), Math.max(3, numbered[2]!.length) + 2);
|
|
507
|
+
const contentWidth = Math.max(1, innerWidth - gutterWidth);
|
|
508
|
+
return Math.max(1, wrapTextWithAnsi(replaceTabs(numbered[3] ?? ""), contentWidth).length);
|
|
509
|
+
}
|
|
510
|
+
|
|
484
511
|
/**
|
|
485
512
|
* Select the trailing physical lines that fit the live preview budget.
|
|
486
513
|
*
|
|
@@ -500,7 +527,7 @@ function sliceStreamingDiffTail(diff: string, innerWidth: number, budget: number
|
|
|
500
527
|
while (cursor >= 0) {
|
|
501
528
|
const newline = cursor > 0 ? diff.lastIndexOf("\n", cursor - 1) : -1;
|
|
502
529
|
const lineStart = newline + 1;
|
|
503
|
-
const lineRows =
|
|
530
|
+
const lineRows = streamingDiffLineRows(diff.slice(lineStart, cursor), innerWidth);
|
|
504
531
|
if (visualRows > 0 && visualRows + lineRows > rowLimit) break;
|
|
505
532
|
visualRows += lineRows;
|
|
506
533
|
start = lineStart;
|
|
@@ -1142,7 +1169,10 @@ export const editToolRenderer = {
|
|
|
1142
1169
|
while (bodyLines.length > 0 && bodyLines[0].trim() === "") bodyLines.shift();
|
|
1143
1170
|
return {
|
|
1144
1171
|
header,
|
|
1145
|
-
sections:
|
|
1172
|
+
sections:
|
|
1173
|
+
bodyLines.length > 0
|
|
1174
|
+
? [{ content: bodyLines.flatMap(line => wrapEditRendererLine(line, Math.max(1, width - 2))) }]
|
|
1175
|
+
: [],
|
|
1146
1176
|
phase: applyPatchError ? "error" : "pending",
|
|
1147
1177
|
borderColor: applyPatchError ? "error" : "borderMuted",
|
|
1148
1178
|
contentPaddingLeft: 0,
|
|
@@ -1192,7 +1222,7 @@ export const editToolRenderer = {
|
|
|
1192
1222
|
diffText = args.previewDiff ?? args.diff ?? "";
|
|
1193
1223
|
body.push(editDiff(diffText, rawPath));
|
|
1194
1224
|
} else if (args.diff || args.newText || args.patch) {
|
|
1195
|
-
body.push(code(args.diff ?? args.newText ?? args.patch ?? "", {
|
|
1225
|
+
body.push(code(args.diff ?? args.newText ?? args.patch ?? "", { path: rawPath || undefined }));
|
|
1196
1226
|
} else if (renderContext?.editDiffPreview) {
|
|
1197
1227
|
const preview = renderContext.editDiffPreview;
|
|
1198
1228
|
if ("error" in preview && preview.error) body.push(errorText(preview.error));
|
|
@@ -1230,8 +1260,18 @@ export const editToolRenderer = {
|
|
|
1230
1260
|
let added = 0;
|
|
1231
1261
|
let removed = 0;
|
|
1232
1262
|
// Files are borderless sections inside the one tool frame.
|
|
1233
|
-
const body: NativeChild[] = perFileResults.map(fileResult => {
|
|
1234
|
-
const
|
|
1263
|
+
const body: NativeChild[] = perFileResults.map((fileResult, index) => {
|
|
1264
|
+
const rawDetails = options.renderContext?.rawDetails;
|
|
1265
|
+
const rawPerFile =
|
|
1266
|
+
rawDetails && typeof rawDetails === "object" && "perFileResults" in rawDetails
|
|
1267
|
+
? rawDetails.perFileResults
|
|
1268
|
+
: undefined;
|
|
1269
|
+
const file = editFileParts(
|
|
1270
|
+
{ content: [], details: fileResult, isError: fileResult.isError },
|
|
1271
|
+
options,
|
|
1272
|
+
undefined,
|
|
1273
|
+
Array.isArray(rawPerFile) ? rawPerFile[index] : undefined,
|
|
1274
|
+
);
|
|
1235
1275
|
added += file.added;
|
|
1236
1276
|
removed += file.removed;
|
|
1237
1277
|
return fileDiffSection(file, file.body, {
|
|
@@ -1304,13 +1344,13 @@ function editToolHead(facts: EditHeadFacts): NativeToolHead {
|
|
|
1304
1344
|
const multi = (facts.files ?? 0) > 1;
|
|
1305
1345
|
const meta = compact([
|
|
1306
1346
|
diffStatsMeta(facts.added, facts.removed),
|
|
1307
|
-
!multi && facts.rename ? `→ ${displayPath(facts.rename)}` : undefined,
|
|
1347
|
+
!multi && facts.rename ? `→ ${displayPath(sanitizeText(facts.rename))}` : undefined,
|
|
1308
1348
|
]);
|
|
1309
1349
|
const badge = facts.diagnostics && diagnosticsBadge(facts.diagnostics);
|
|
1310
1350
|
const target = multi
|
|
1311
1351
|
? `${facts.files} files`
|
|
1312
1352
|
: facts.path
|
|
1313
|
-
? `${displayPath(facts.path)}${facts.line ? `:${facts.line}` : ""}`
|
|
1353
|
+
? `${displayPath(sanitizeText(facts.path))}${facts.line ? `:${facts.line}` : ""}`
|
|
1314
1354
|
: undefined;
|
|
1315
1355
|
return {
|
|
1316
1356
|
title: multi ? "Edit" : getOperationTitle(facts.op),
|
|
@@ -1322,9 +1362,9 @@ function editToolHead(facts: EditHeadFacts): NativeToolHead {
|
|
|
1322
1362
|
};
|
|
1323
1363
|
}
|
|
1324
1364
|
|
|
1325
|
-
/** A file's diff: hunks
|
|
1365
|
+
/** A file's diff: Tern highlights the hunks by the file's grammar and draws no path header (the head or section names the file). */
|
|
1326
1366
|
function editDiff(diffText: string, filePath: string): NativeNode {
|
|
1327
|
-
return nativeDiff(diffText, {
|
|
1367
|
+
return nativeDiff(diffText, { path: filePath || undefined });
|
|
1328
1368
|
}
|
|
1329
1369
|
|
|
1330
1370
|
/** One file's edit result for native views: head facts plus its body (diff or error, then diagnostics). */
|
|
@@ -1337,6 +1377,15 @@ interface EditFileParts extends Omit<EditHeadFacts, "diagnostics"> {
|
|
|
1337
1377
|
body: NativeChild[];
|
|
1338
1378
|
}
|
|
1339
1379
|
|
|
1380
|
+
/** Raw metadata paths are reserved for link targets; visible content comes from sanitized details. */
|
|
1381
|
+
function rawEditPaths(value: unknown): { path?: string; sourcePath?: string } {
|
|
1382
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return {};
|
|
1383
|
+
return {
|
|
1384
|
+
path: "path" in value && typeof value.path === "string" ? value.path : undefined,
|
|
1385
|
+
sourcePath: "sourcePath" in value && typeof value.sourcePath === "string" ? value.sourcePath : undefined,
|
|
1386
|
+
};
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1340
1389
|
function editFileParts(
|
|
1341
1390
|
result: {
|
|
1342
1391
|
content: Array<{ type: string; text?: string }>;
|
|
@@ -1345,8 +1394,10 @@ function editFileParts(
|
|
|
1345
1394
|
},
|
|
1346
1395
|
options: RenderResultOptions & { renderContext?: EditRenderContext },
|
|
1347
1396
|
args?: EditRenderArgs,
|
|
1397
|
+
rawFileDetails?: unknown,
|
|
1348
1398
|
): EditFileParts {
|
|
1349
1399
|
const details = result.details;
|
|
1400
|
+
const rawPaths = rawEditPaths(rawFileDetails ?? options.renderContext?.rawDetails);
|
|
1350
1401
|
const isError = result.isError ?? (details && "isError" in details ? details.isError : false) ?? false;
|
|
1351
1402
|
const firstEdit = Array.isArray(args?.edits) ? args.edits[0] : undefined;
|
|
1352
1403
|
const firstHashlineInputEntry = getHashlineInputRenderSummary(args ?? {}, options.renderContext?.editMode)
|
|
@@ -1373,7 +1424,7 @@ function editFileParts(
|
|
|
1373
1424
|
const firstChangedLine =
|
|
1374
1425
|
(editDiffPreview && "firstChangedLine" in editDiffPreview ? editDiffPreview.firstChangedLine : undefined) ||
|
|
1375
1426
|
(details && !isError ? details.firstChangedLine : undefined);
|
|
1376
|
-
const shownPath = displayPath(detailPath ?? rawPath);
|
|
1427
|
+
const shownPath = displayPath(sanitizeText(detailPath ?? rawPath));
|
|
1377
1428
|
const body: NativeChild[] = [];
|
|
1378
1429
|
if (isError) {
|
|
1379
1430
|
const displayErrorText = details && "displayErrorText" in details ? details.displayErrorText : undefined;
|
|
@@ -1392,10 +1443,10 @@ function editFileParts(
|
|
|
1392
1443
|
const stats = diffText ? getDiffStats(diffText) : undefined;
|
|
1393
1444
|
return {
|
|
1394
1445
|
op,
|
|
1395
|
-
path: rawPath,
|
|
1446
|
+
path: sanitizeText(rawPath),
|
|
1396
1447
|
line: firstChangedLine,
|
|
1397
|
-
rename,
|
|
1398
|
-
resolvedPath: detailPath,
|
|
1448
|
+
rename: rename === undefined ? undefined : sanitizeText(rename),
|
|
1449
|
+
resolvedPath: rawPaths.path ?? detailPath,
|
|
1399
1450
|
added: stats?.added ?? 0,
|
|
1400
1451
|
removed: stats?.removed ?? 0,
|
|
1401
1452
|
isError: Boolean(isError),
|
|
@@ -1415,6 +1466,7 @@ function renderSingleFileResult(
|
|
|
1415
1466
|
args?: EditRenderArgs,
|
|
1416
1467
|
): Component {
|
|
1417
1468
|
const details = result.details;
|
|
1469
|
+
const rawPaths = rawEditPaths(options.renderContext?.rawDetails);
|
|
1418
1470
|
const isError = result.isError ?? (details && "isError" in details ? details.isError : false);
|
|
1419
1471
|
const edits = Array.isArray(args?.edits) ? args.edits : undefined;
|
|
1420
1472
|
const firstEdit = edits?.[0];
|
|
@@ -1449,8 +1501,17 @@ function renderSingleFileResult(
|
|
|
1449
1501
|
// container. Errors, no-ops, creates, move-with-edits, and anything with
|
|
1450
1502
|
// diagnostics keep the framed block below.
|
|
1451
1503
|
if (!isError && !details?.diff && !details?.diagnostics && (op === "delete" || rename)) {
|
|
1452
|
-
const linkPath =
|
|
1453
|
-
|
|
1504
|
+
const linkPath = rename
|
|
1505
|
+
? (rawPaths.sourcePath ?? moveSource ?? rawPaths.path ?? detailPath)
|
|
1506
|
+
: (rawPaths.path ?? detailPath);
|
|
1507
|
+
return renderInlineEditRow(uiTheme, {
|
|
1508
|
+
op,
|
|
1509
|
+
rename,
|
|
1510
|
+
rawPath,
|
|
1511
|
+
linkPath,
|
|
1512
|
+
renameLinkPath: rename ? (rawPaths.path ?? detailPath) : undefined,
|
|
1513
|
+
pending: false,
|
|
1514
|
+
});
|
|
1454
1515
|
}
|
|
1455
1516
|
|
|
1456
1517
|
const renderFallbackDiff = (diffText: string, diffOptions?: { filePath?: string }): string =>
|
|
@@ -1481,7 +1542,9 @@ function renderSingleFileResult(
|
|
|
1481
1542
|
const firstChangedLine =
|
|
1482
1543
|
(editDiffPreview && "firstChangedLine" in editDiffPreview ? editDiffPreview.firstChangedLine : undefined) ||
|
|
1483
1544
|
(details && !isError ? details.firstChangedLine : undefined);
|
|
1484
|
-
const linkPath =
|
|
1545
|
+
const linkPath = rename
|
|
1546
|
+
? (rawPaths.sourcePath ?? moveSource ?? rawPaths.path ?? detailPath)
|
|
1547
|
+
: (rawPaths.path ?? detailPath);
|
|
1485
1548
|
|
|
1486
1549
|
// Change stats ride inline on the header bar next to the path.
|
|
1487
1550
|
const previewDiff = editDiffPreview && !("error" in editDiffPreview) ? editDiffPreview.diff : undefined;
|
|
@@ -1499,6 +1562,7 @@ function renderSingleFileResult(
|
|
|
1499
1562
|
rename,
|
|
1500
1563
|
firstChangedLine,
|
|
1501
1564
|
linkPath,
|
|
1565
|
+
renameLinkPath: rename ? (rawPaths.path ?? detailPath) : undefined,
|
|
1502
1566
|
statsSuffix,
|
|
1503
1567
|
});
|
|
1504
1568
|
const innerWidth = Math.max(1, width - 2);
|
|
@@ -1523,7 +1587,11 @@ function renderSingleFileResult(
|
|
|
1523
1587
|
// (Delete / `src → dst`); only a true no-op needs an explanatory
|
|
1524
1588
|
// body so an empty card isn't mistaken for a stalled edit.
|
|
1525
1589
|
if (op !== "delete" && op !== "create" && !rename) {
|
|
1526
|
-
const noChangePath =
|
|
1590
|
+
const noChangePath = detailPath
|
|
1591
|
+
? shortenPath(detailPath)
|
|
1592
|
+
: rawPath
|
|
1593
|
+
? shortenPath(sanitizeText(rawPath))
|
|
1594
|
+
: "";
|
|
1527
1595
|
body = uiTheme.fg("dim", `No changes were made${noChangePath ? ` to ${noChangePath}` : ""}.`);
|
|
1528
1596
|
}
|
|
1529
1597
|
} else if (editDiffPreview) {
|
|
@@ -1575,8 +1643,19 @@ function renderMultiFileResult(
|
|
|
1575
1643
|
options: RenderResultOptions & { renderContext?: EditRenderContext },
|
|
1576
1644
|
uiTheme: Theme,
|
|
1577
1645
|
): Component {
|
|
1578
|
-
const
|
|
1579
|
-
|
|
1646
|
+
const rawDetails = options.renderContext?.rawDetails;
|
|
1647
|
+
const rawPerFile =
|
|
1648
|
+
rawDetails && typeof rawDetails === "object" && "perFileResults" in rawDetails
|
|
1649
|
+
? rawDetails.perFileResults
|
|
1650
|
+
: undefined;
|
|
1651
|
+
const fileComponents = perFileResults.map((fileResult, index) =>
|
|
1652
|
+
renderSingleFileResult(
|
|
1653
|
+
{ content: [], details: fileResult, isError: fileResult.isError },
|
|
1654
|
+
Array.isArray(rawPerFile)
|
|
1655
|
+
? { ...options, renderContext: { ...options.renderContext, rawDetails: rawPerFile[index] } }
|
|
1656
|
+
: options,
|
|
1657
|
+
uiTheme,
|
|
1658
|
+
),
|
|
1580
1659
|
);
|
|
1581
1660
|
const remaining = Math.max(0, totalFiles - perFileResults.length);
|
|
1582
1661
|
|