pi-pretty-tui 0.2.9 → 0.3.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/README.md +33 -13
- package/README.zh-CN.md +33 -13
- package/extensions/ansi-style.ts +29 -0
- package/extensions/card-surface.ts +38 -0
- package/extensions/full-frame.ts +134 -0
- package/extensions/index.ts +360 -231
- package/extensions/nested-tools.ts +59 -13
- package/extensions/shell-highlight.ts +36 -0
- package/extensions/thought-presentation.ts +38 -0
- package/extensions/tool-presentation.ts +286 -0
- package/package.json +1 -1
package/extensions/index.ts
CHANGED
|
@@ -1,18 +1,30 @@
|
|
|
1
|
+
import { isNativeImageRow } from "./ansi-style.js";
|
|
2
|
+
import { cardMargin, cardBackground, frameNativeRows, nativeFrameWidth } from "./full-frame.js";
|
|
3
|
+
import { ThoughtPresentation } from "./thought-presentation.js";
|
|
4
|
+
import { isThirdPartyTool, renderToolProjection, toolLabel, type ToolProjection } from "./tool-presentation.js";
|
|
1
5
|
import { NestedTools, NESTED_SNAPSHOT_ENTRY } from "./nested-tools.js";
|
|
2
6
|
import {
|
|
3
7
|
AssistantMessageComponent,
|
|
4
8
|
CustomEditor,
|
|
5
9
|
CustomMessageComponent,
|
|
10
|
+
CompactionSummaryMessageComponent,
|
|
6
11
|
InteractiveMode,
|
|
7
12
|
ToolExecutionComponent,
|
|
8
13
|
UserMessageComponent,
|
|
9
14
|
createBashTool,
|
|
15
|
+
createBashToolDefinition,
|
|
10
16
|
createEditTool,
|
|
17
|
+
createEditToolDefinition,
|
|
11
18
|
createFindTool,
|
|
19
|
+
createFindToolDefinition,
|
|
12
20
|
createGrepTool,
|
|
21
|
+
createGrepToolDefinition,
|
|
13
22
|
createLsTool,
|
|
23
|
+
createLsToolDefinition,
|
|
14
24
|
createReadTool,
|
|
25
|
+
createReadToolDefinition,
|
|
15
26
|
createWriteTool,
|
|
27
|
+
createWriteToolDefinition,
|
|
16
28
|
copyToClipboard,
|
|
17
29
|
getAgentDir,
|
|
18
30
|
getMarkdownTheme,
|
|
@@ -46,26 +58,7 @@ type PrettyTuiMode = "full" | "compact" | "clean";
|
|
|
46
58
|
const CLEAN_TOOL_ACTIVITY_MIN_MS = 1000;
|
|
47
59
|
const TRANSIENT_UI_STATUS_MS = 10_000;
|
|
48
60
|
const RESPONSE_FOOTER_ENTRY_TYPE = "pretty-tui-response-footer";
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
const stripAnsiBackgrounds = (value: string): string =>
|
|
52
|
-
value.replace(/\x1b\[([0-9:;]*)m/g, (sequence, raw: string) => {
|
|
53
|
-
const fields = raw === "" ? [0] : raw.split(";").map((field) => Number(field));
|
|
54
|
-
const kept: number[] = [];
|
|
55
|
-
for (let index = 0; index < fields.length; index++) {
|
|
56
|
-
const field = fields[index];
|
|
57
|
-
if ((field >= 40 && field <= 49) || (field >= 100 && field <= 107)) continue;
|
|
58
|
-
if (field === 48) {
|
|
59
|
-
const mode = fields[index + 1];
|
|
60
|
-
if (mode === 5) index += 2;
|
|
61
|
-
else if (mode === 2) index += 4;
|
|
62
|
-
continue;
|
|
63
|
-
}
|
|
64
|
-
kept.push(field);
|
|
65
|
-
}
|
|
66
|
-
if (raw.includes(":") && /(?:^|;)48:/u.test(raw)) return "";
|
|
67
|
-
return kept.length > 0 ? `\x1b[${kept.join(";")}m` : "";
|
|
68
|
-
});
|
|
61
|
+
|
|
69
62
|
type PrettyTuiConfig = {
|
|
70
63
|
enabled?: boolean;
|
|
71
64
|
mode?: PrettyTuiMode;
|
|
@@ -95,6 +88,10 @@ export default function prettyTui(pi: ExtensionAPI) {
|
|
|
95
88
|
? configuredMode
|
|
96
89
|
: "clean";
|
|
97
90
|
let cleanToolsExpanded = false;
|
|
91
|
+
// Full starts with thoughts open; an intentional Ctrl+T overrides that
|
|
92
|
+
// default for this runtime, independently of compact's initial setting.
|
|
93
|
+
let fullThinkingHidden = false;
|
|
94
|
+
let compactThinkingHidden = true;
|
|
98
95
|
let cleanContextCompacted = false;
|
|
99
96
|
let fullscreenTui = false;
|
|
100
97
|
const mouseClickHitTests = new WeakMap<object, (event: any) => boolean>();
|
|
@@ -159,7 +156,7 @@ export default function prettyTui(pi: ExtensionAPI) {
|
|
|
159
156
|
};
|
|
160
157
|
const refreshLatestActivityWidget = () => {
|
|
161
158
|
if (!currentExtensionUi?.setWidget) return;
|
|
162
|
-
if (
|
|
159
|
+
if (!latestActivityStatus) {
|
|
163
160
|
if (latestActivityWidgetInstalled) {
|
|
164
161
|
currentExtensionUi.setWidget(latestActivityWidgetKey, undefined);
|
|
165
162
|
latestActivityWidgetInstalled = false;
|
|
@@ -261,7 +258,9 @@ export default function prettyTui(pi: ExtensionAPI) {
|
|
|
261
258
|
const activityTimeline = new ActivityTimeline();
|
|
262
259
|
const nestedTools = new NestedTools();
|
|
263
260
|
const refreshNestedTools = (id: string) => {
|
|
264
|
-
|
|
261
|
+
const owner = toolComponents.get(nestedTools.root(id));
|
|
262
|
+
owner?.updateDisplay?.();
|
|
263
|
+
owner?.ui?.requestRender?.();
|
|
265
264
|
currentTui?.requestRender?.();
|
|
266
265
|
};
|
|
267
266
|
const revealedActivityGroups = new Set<string>();
|
|
@@ -283,7 +282,7 @@ export default function prettyTui(pi: ExtensionAPI) {
|
|
|
283
282
|
const cleanCompactToolCallIds = new Set<string>();
|
|
284
283
|
const cleanGroupToolCallIds = new Map<string, string[]>();
|
|
285
284
|
const cleanToolCallGroupOwners = new Map<string, string>();
|
|
286
|
-
const
|
|
285
|
+
const toolComponents = new Map<string, any>();
|
|
287
286
|
const cleanToolThemes = new Map<string, any>();
|
|
288
287
|
const cleanToolNames = new Map<string, string>();
|
|
289
288
|
let renderCleanGroupSummary = (_lastToolCallId: string, _width: number): string[] => [];
|
|
@@ -355,12 +354,14 @@ export default function prettyTui(pi: ExtensionAPI) {
|
|
|
355
354
|
return group.toolCallIds[group.toolCallIds.length - 1];
|
|
356
355
|
};
|
|
357
356
|
|
|
357
|
+
let fallbackActivityTheme: any;
|
|
358
358
|
const defaultActivityTheme = () => {
|
|
359
359
|
if (currentExtensionUi?.theme?.fg) return currentExtensionUi.theme;
|
|
360
360
|
const markdownTheme = getMarkdownTheme();
|
|
361
|
-
return {
|
|
361
|
+
return fallbackActivityTheme ??= {
|
|
362
362
|
fg: (color: string, text: string) => {
|
|
363
363
|
if (color === "accent") return markdownTheme.listBullet(text);
|
|
364
|
+
if (color === "mdHeading") return markdownTheme.heading(text);
|
|
364
365
|
// Session UI normally supplies Pi's active Theme above. Keep portable
|
|
365
366
|
// semantic fallbacks for early startup and isolated renderer tests.
|
|
366
367
|
if (color === "warning") return `\x1b[33m${text}\x1b[39m`;
|
|
@@ -443,7 +444,7 @@ export default function prettyTui(pi: ExtensionAPI) {
|
|
|
443
444
|
const refreshActivityGroup = (group: ActivityGroup): void => {
|
|
444
445
|
for (const child of group.members) {
|
|
445
446
|
if (child.kind === "tool" && child.toolCallId) {
|
|
446
|
-
|
|
447
|
+
toolComponents.get(child.toolCallId)?.updateDisplay?.();
|
|
447
448
|
} else if (child.kind === "thinking" && child.messageKey) {
|
|
448
449
|
thinkingComponents.get(child.messageKey)?.invalidate?.();
|
|
449
450
|
} else if (child.kind === "update") {
|
|
@@ -468,7 +469,7 @@ export default function prettyTui(pi: ExtensionAPI) {
|
|
|
468
469
|
else collapsedActivityGroups.delete(group.id);
|
|
469
470
|
for (const toolCallId of group.toolCallIds) {
|
|
470
471
|
cleanCompactToolCallIds.delete(toolCallId);
|
|
471
|
-
const component =
|
|
472
|
+
const component = toolComponents.get(toolCallId);
|
|
472
473
|
if (component?.expanded) component.setExpanded(false);
|
|
473
474
|
}
|
|
474
475
|
for (const child of group.members) expandedThinkingMembers.delete(child.id);
|
|
@@ -580,7 +581,7 @@ export default function prettyTui(pi: ExtensionAPI) {
|
|
|
580
581
|
};
|
|
581
582
|
|
|
582
583
|
const renderStandaloneError = (member: ActivityMember, width: number, component?: any): string[] => {
|
|
583
|
-
const run = component ? errorRun(component) : { hidden: false, count: 1, compact: false };
|
|
584
|
+
const run = component && renderMode !== "full" ? errorRun(component) : { hidden: false, count: 1, compact: false };
|
|
584
585
|
if (run.hidden) return [];
|
|
585
586
|
const theme = defaultActivityTheme();
|
|
586
587
|
const style = activityUpdateStyle(member, theme);
|
|
@@ -624,7 +625,7 @@ export default function prettyTui(pi: ExtensionAPI) {
|
|
|
624
625
|
width: number,
|
|
625
626
|
component?: any,
|
|
626
627
|
): string[] => {
|
|
627
|
-
if (!activityGroupCollapsible(group)) return renderPendingActivityUpdate(member, width, component);
|
|
628
|
+
if (renderMode !== "clean" || !activityGroupCollapsible(group)) return renderPendingActivityUpdate(member, width, component);
|
|
628
629
|
const position = activityMemberPosition(group, member);
|
|
629
630
|
if (!activityGroupRevealed(group)) {
|
|
630
631
|
return position.first
|
|
@@ -640,7 +641,7 @@ export default function prettyTui(pi: ExtensionAPI) {
|
|
|
640
641
|
event: any,
|
|
641
642
|
) => {
|
|
642
643
|
const isLeftClick = event.type === "click" && event.button === "left";
|
|
643
|
-
if (!isLeftClick || !activityGroupCollapsible(group)) return undefined;
|
|
644
|
+
if (renderMode !== "clean" || !isLeftClick || !activityGroupCollapsible(group)) return undefined;
|
|
644
645
|
const position = activityMemberPosition(group, member);
|
|
645
646
|
if (!activityGroupRevealed(group)) {
|
|
646
647
|
if (!position.first) return undefined;
|
|
@@ -693,7 +694,7 @@ export default function prettyTui(pi: ExtensionAPI) {
|
|
|
693
694
|
}
|
|
694
695
|
const enabled = `Enabled — ${config.enabled !== false ? "on" : "off"}`;
|
|
695
696
|
const full = `Full — full tool details and output${renderMode === "full" ? " (current)" : ""}`;
|
|
696
|
-
const compact = `Compact — concise summaries
|
|
697
|
+
const compact = `Compact — concise tool summaries and nested calls${renderMode === "compact" ? " (current)" : ""}`;
|
|
697
698
|
const clean = `Clean — group supported tools into Running/Done status${renderMode === "clean" ? " (current)" : ""}`;
|
|
698
699
|
const selected = await ctx.ui.select("pi-pretty-tui settings", [enabled, full, compact, clean]);
|
|
699
700
|
if (!selected) return;
|
|
@@ -745,14 +746,9 @@ export default function prettyTui(pi: ExtensionAPI) {
|
|
|
745
746
|
collapsedActivityGroups.clear();
|
|
746
747
|
expandedThinkingMembers.clear();
|
|
747
748
|
renderMode = requested;
|
|
748
|
-
if (
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
} else {
|
|
752
|
-
stopWorkingElapsedTimer();
|
|
753
|
-
clearLatestActivityStatus();
|
|
754
|
-
}
|
|
755
|
-
for (const component of cleanToolComponents.values()) component.updateDisplay?.();
|
|
749
|
+
if (cleanRun.active && !cleanRun.settled) startWorkingElapsedTimer();
|
|
750
|
+
refreshLatestActivityWidget();
|
|
751
|
+
for (const component of toolComponents.values()) component.updateDisplay?.();
|
|
756
752
|
for (const component of thinkingComponents.values()) component.invalidate?.();
|
|
757
753
|
try {
|
|
758
754
|
saveRenderMode(renderMode);
|
|
@@ -784,7 +780,7 @@ export default function prettyTui(pi: ExtensionAPI) {
|
|
|
784
780
|
if (this.tui) currentTui = this.tui;
|
|
785
781
|
currentPromptEditor = this;
|
|
786
782
|
const indicator = this.workingStatusIndicator;
|
|
787
|
-
if (
|
|
783
|
+
if (indicator?.kind !== "working" || responseRunStartedAt === undefined) {
|
|
788
784
|
return originalEditorTopBorder.call(this, width, hiddenLineCount);
|
|
789
785
|
}
|
|
790
786
|
const elapsed = formatResponseDuration(Date.now() - responseRunStartedAt);
|
|
@@ -872,10 +868,8 @@ export default function prettyTui(pi: ExtensionAPI) {
|
|
|
872
868
|
});
|
|
873
869
|
}
|
|
874
870
|
|
|
875
|
-
//
|
|
876
|
-
//
|
|
877
|
-
// zones. Long messages stop at 75% of the terminal width; narrow terminals
|
|
878
|
-
// trade away the left gap before sacrificing readability.
|
|
871
|
+
// Clean uses content-sized bubbles; compact/full share full-width panels.
|
|
872
|
+
// Keep Pi's Markdown, mouse handling and OSC 133 zones in every mode.
|
|
879
873
|
const userMessagePrototype = UserMessageComponent.prototype as any;
|
|
880
874
|
const userMessagePatchKey = Symbol.for("pretty-tui.user-message-frame");
|
|
881
875
|
if (!userMessagePrototype[userMessagePatchKey]) {
|
|
@@ -912,9 +906,9 @@ export default function prettyTui(pi: ExtensionAPI) {
|
|
|
912
906
|
.map((line: string) => line.replace(/ +$/u, ""));
|
|
913
907
|
const layout = (width: number) => {
|
|
914
908
|
const terminalWidth = Math.max(1, width);
|
|
915
|
-
const rightPad = Math.min(outputPad, Math.max(0, terminalWidth - 1));
|
|
909
|
+
const rightPad = renderMode !== "clean" ? 0 : Math.min(outputPad, Math.max(0, terminalWidth - 1));
|
|
916
910
|
const availableWidth = Math.max(1, terminalWidth - rightPad);
|
|
917
|
-
if (availableWidth < 4) {
|
|
911
|
+
if (availableWidth < (renderMode !== "clean" ? 8 : 4)) {
|
|
918
912
|
return {
|
|
919
913
|
framed: false,
|
|
920
914
|
rightPad,
|
|
@@ -925,6 +919,16 @@ export default function prettyTui(pi: ExtensionAPI) {
|
|
|
925
919
|
};
|
|
926
920
|
}
|
|
927
921
|
|
|
922
|
+
if (renderMode !== "clean") {
|
|
923
|
+
const margin = cardMargin(terminalWidth);
|
|
924
|
+
const contentWidth = nativeFrameWidth(terminalWidth);
|
|
925
|
+
return {
|
|
926
|
+
framed: true, rightPad: margin, leftPad: margin,
|
|
927
|
+
frameWidth: terminalWidth - margin * 2, contentWidth,
|
|
928
|
+
body: renderMarkdown(contentWidth),
|
|
929
|
+
};
|
|
930
|
+
}
|
|
931
|
+
|
|
928
932
|
const preferredMaximum = Math.max(4, Math.floor(terminalWidth * 0.75));
|
|
929
933
|
// Keep at least one blank column on the left whenever a framed bubble
|
|
930
934
|
// fits, even after a narrow terminal relaxes the usual 75% maximum.
|
|
@@ -955,8 +959,8 @@ export default function prettyTui(pi: ExtensionAPI) {
|
|
|
955
959
|
};
|
|
956
960
|
};
|
|
957
961
|
const border = (text: string) => currentExtensionUi?.theme?.fg
|
|
958
|
-
? currentExtensionUi.theme.fg("
|
|
959
|
-
: markdownTheme.
|
|
962
|
+
? currentExtensionUi.theme.fg("mdHeading", text)
|
|
963
|
+
: markdownTheme.heading(text);
|
|
960
964
|
const userText = typeof this.text === "string" ? this.text : "";
|
|
961
965
|
const frame: Component = {
|
|
962
966
|
render(width: number): string[] {
|
|
@@ -964,7 +968,12 @@ export default function prettyTui(pi: ExtensionAPI) {
|
|
|
964
968
|
const left = " ".repeat(measured.leftPad);
|
|
965
969
|
const right = " ".repeat(measured.rightPad);
|
|
966
970
|
if (!measured.framed) {
|
|
967
|
-
return measured.body.map((line: string) =>
|
|
971
|
+
return measured.body.map((line: string) => renderMode !== "clean"
|
|
972
|
+
? cardBackground(line, width, defaultActivityTheme(), "toolPendingBg", "mdHeading") : left + line + right);
|
|
973
|
+
}
|
|
974
|
+
if (renderMode !== "clean") {
|
|
975
|
+
return frameNativeRows("User", measured.body, width, defaultActivityTheme(), undefined,
|
|
976
|
+
frame, "mdHeading", "", "mdHeading").lines;
|
|
968
977
|
}
|
|
969
978
|
|
|
970
979
|
const top = border("╭" + "─".repeat(Math.max(0, measured.frameWidth - 2)) + "╮");
|
|
@@ -1106,6 +1115,7 @@ export default function prettyTui(pi: ExtensionAPI) {
|
|
|
1106
1115
|
return true;
|
|
1107
1116
|
};
|
|
1108
1117
|
const patchedCustomRender = function (this: any, width: number): string[] {
|
|
1118
|
+
if (renderMode === "full") return originalCustomRender.call(this, width);
|
|
1109
1119
|
if (releaseSemanticCustomMessage(this)) {
|
|
1110
1120
|
return originalCustomRender.call(this, width);
|
|
1111
1121
|
}
|
|
@@ -1116,23 +1126,24 @@ export default function prettyTui(pi: ExtensionAPI) {
|
|
|
1116
1126
|
// message_start event. Claim them at the component boundary so live
|
|
1117
1127
|
// rendering matches transcript restoration instead of failing open to
|
|
1118
1128
|
// the purple native box until the next reload.
|
|
1119
|
-
if (!member
|
|
1129
|
+
if (!member) {
|
|
1120
1130
|
member = addPersistentActivityUpdate(message);
|
|
1121
1131
|
}
|
|
1122
1132
|
const group = member ? activityTimeline.groupForMember(member.id) : undefined;
|
|
1123
|
-
if (
|
|
1133
|
+
if (!member || !group) {
|
|
1124
1134
|
return originalCustomRender.call(this, width);
|
|
1125
1135
|
}
|
|
1126
1136
|
activityUpdateComponents.set(member.id, this);
|
|
1127
1137
|
return renderActivityUpdateProjection(group, member, width);
|
|
1128
1138
|
};
|
|
1129
1139
|
const patchedCustomHandleMouse = function (this: any, event: any) {
|
|
1140
|
+
if (renderMode === "full") return originalCustomHandleMouse?.call(this, event);
|
|
1130
1141
|
if (releaseSemanticCustomMessage(this)) {
|
|
1131
1142
|
return originalCustomHandleMouse?.call(this, event);
|
|
1132
1143
|
}
|
|
1133
1144
|
const member = activityTimeline.memberForUpdate(customMessageKey(this.message));
|
|
1134
1145
|
const group = member ? activityTimeline.groupForMember(member.id) : undefined;
|
|
1135
|
-
if (
|
|
1146
|
+
if (!member || !group) {
|
|
1136
1147
|
return originalCustomHandleMouse?.call(this, event);
|
|
1137
1148
|
}
|
|
1138
1149
|
return handleActivityUpdateMouse(group, member, event);
|
|
@@ -1163,20 +1174,79 @@ export default function prettyTui(pi: ExtensionAPI) {
|
|
|
1163
1174
|
});
|
|
1164
1175
|
}
|
|
1165
1176
|
|
|
1177
|
+
// Keep Pi's compaction content and expansion state inside the shared panel.
|
|
1178
|
+
const compactionPrototype = CompactionSummaryMessageComponent.prototype as any;
|
|
1179
|
+
const compactionPatchKey = Symbol.for("pretty-tui.compaction-panel");
|
|
1180
|
+
if (!compactionPrototype[compactionPatchKey]) {
|
|
1181
|
+
const originalRender = compactionPrototype.render;
|
|
1182
|
+
const originalHandleMouse = compactionPrototype.handleMouse;
|
|
1183
|
+
const views = new WeakMap<object, { width: number; sourceOffset: number; frame: ReturnType<typeof frameNativeRows> }>();
|
|
1184
|
+
const render = function (this: any, width: number): string[] {
|
|
1185
|
+
if (renderMode === "clean") {
|
|
1186
|
+
views.delete(this);
|
|
1187
|
+
mouseClickHitTests.delete(this);
|
|
1188
|
+
return originalRender.call(this, width);
|
|
1189
|
+
}
|
|
1190
|
+
const innerWidth = nativeFrameWidth(width);
|
|
1191
|
+
const region = this.children[0];
|
|
1192
|
+
// Native content begins with its [compaction] label and a spacer. The
|
|
1193
|
+
// panel title replaces those two rows; the summary itself is untouched.
|
|
1194
|
+
const content = region.child.children;
|
|
1195
|
+
const sourceOffset = content.slice(0, 2).reduce((height: number, child: any) => height + child.render(innerWidth).length, 0);
|
|
1196
|
+
const source = content.slice(2).flatMap((child: any) => child.render(innerWidth));
|
|
1197
|
+
const theme = defaultActivityTheme();
|
|
1198
|
+
const frame = frameNativeRows(`compaction ${this.expanded ? "▾" : "▸"}`, source, width, theme,
|
|
1199
|
+
undefined, this, "customMessageLabel", "", "customMessageLabel", "customMessageBg");
|
|
1200
|
+
views.set(this, { width: innerWidth, sourceOffset, frame });
|
|
1201
|
+
mouseClickHitTests.set(this, (event) => event.y === 0 || frame.sourceRows.has(event.y));
|
|
1202
|
+
return frame.lines.map((line) => isNativeImageRow(line) ? line : truncateToWidth(line, Math.max(1, width), ""));
|
|
1203
|
+
};
|
|
1204
|
+
const handleMouse = function (this: any, event: any) {
|
|
1205
|
+
if (renderMode === "clean") return originalHandleMouse?.call(this, event);
|
|
1206
|
+
render.call(this, event.width);
|
|
1207
|
+
const view = views.get(this);
|
|
1208
|
+
if (!view) return undefined;
|
|
1209
|
+
if (event.y === 0 && event.type === "click" && event.button === "left") {
|
|
1210
|
+
this.setExpanded(!this.expanded);
|
|
1211
|
+
currentTui?.requestRender?.();
|
|
1212
|
+
return { handled: true };
|
|
1213
|
+
}
|
|
1214
|
+
const sourceY = view.frame.sourceRows.get(event.y);
|
|
1215
|
+
if (sourceY === undefined) return undefined;
|
|
1216
|
+
const result = this.children[0].handleMouse?.({ ...event,
|
|
1217
|
+
x: Math.max(0, event.x - view.frame.inset), y: sourceY + view.sourceOffset,
|
|
1218
|
+
width: view.width, height: view.frame.sourceRows.size + view.sourceOffset,
|
|
1219
|
+
});
|
|
1220
|
+
if (result?.handled) currentTui?.requestRender?.();
|
|
1221
|
+
return result;
|
|
1222
|
+
};
|
|
1223
|
+
compactionPrototype[compactionPatchKey] = { originalRender, originalHandleMouse };
|
|
1224
|
+
compactionPrototype.render = render;
|
|
1225
|
+
compactionPrototype.handleMouse = handleMouse;
|
|
1226
|
+
pi.on("session_shutdown", () => {
|
|
1227
|
+
if (compactionPrototype.render === render) compactionPrototype.render = originalRender;
|
|
1228
|
+
if (compactionPrototype.handleMouse === handleMouse) compactionPrototype.handleMouse = originalHandleMouse;
|
|
1229
|
+
if (compactionPrototype.render === originalRender && compactionPrototype.handleMouse === originalHandleMouse) {
|
|
1230
|
+
delete compactionPrototype[compactionPatchKey];
|
|
1231
|
+
}
|
|
1232
|
+
});
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1166
1235
|
// Thinking and tools are peers in the transcript-first activity timeline.
|
|
1167
|
-
// Assistant components keep Pi's native Markdown renderer
|
|
1168
|
-
//
|
|
1236
|
+
// Assistant components keep Pi's native visible-answer Markdown renderer.
|
|
1237
|
+
// All modes share thought content; only clean adds activity-group framing.
|
|
1169
1238
|
const assistantPrototype = AssistantMessageComponent.prototype as any;
|
|
1170
1239
|
const thinkingPatchKey = Symbol.for("pretty-tui.clean-thinking");
|
|
1171
1240
|
if (!assistantPrototype[thinkingPatchKey]) {
|
|
1172
1241
|
const originalUpdateContent = assistantPrototype.updateContent;
|
|
1173
1242
|
const originalRender = assistantPrototype.render;
|
|
1174
1243
|
const originalHandleMouse = assistantPrototype.handleMouse;
|
|
1244
|
+
const originalSetHideThinkingBlock = assistantPrototype.setHideThinkingBlock;
|
|
1245
|
+
const explicitThinkingVisibility = new WeakMap<object, boolean>();
|
|
1175
1246
|
const originalMessageKey = Symbol("pretty-tui.original-assistant-message");
|
|
1176
1247
|
const renderedModeKey = Symbol("pretty-tui.rendered-assistant-mode");
|
|
1177
1248
|
const renderedExpansionKey = Symbol("pretty-tui.rendered-assistant-expansion");
|
|
1178
1249
|
const cleanThinkingRenderCacheKey = Symbol("pretty-tui.clean-thinking-render-cache");
|
|
1179
|
-
const cleanThinkingDetailKey = Symbol("pretty-tui.clean-thinking-detail");
|
|
1180
1250
|
|
|
1181
1251
|
const assistantTerminalUpdate = (message: any): ActivityMember | undefined => {
|
|
1182
1252
|
if (!assistantTerminalState(message)) return undefined;
|
|
@@ -1216,32 +1286,84 @@ export default function prettyTui(pi: ExtensionAPI) {
|
|
|
1216
1286
|
currentTui?.requestRender?.();
|
|
1217
1287
|
};
|
|
1218
1288
|
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1289
|
+
const thoughtPresentation = new ThoughtPresentation();
|
|
1290
|
+
const fullThinkingVisibility = new WeakMap<object, boolean>();
|
|
1291
|
+
const patchedSetHideThinkingBlock = function (this: any, hide: boolean) {
|
|
1292
|
+
if (renderMode !== "clean") explicitThinkingVisibility.set(this, !hide);
|
|
1293
|
+
if (renderMode === "full") fullThinkingVisibility.set(this, !hide);
|
|
1294
|
+
return originalSetHideThinkingBlock.call(this, hide);
|
|
1295
|
+
};
|
|
1296
|
+
const panelAssistantViews = new WeakMap<object, { rows: Map<number, { sourceY: number; inset: number }>; width: number; height: number }>();
|
|
1297
|
+
const renderPanelAssistant = function (this: any, width: number): string[] {
|
|
1298
|
+
const innerWidth = nativeFrameWidth(width);
|
|
1299
|
+
const nativeLines = originalRender.call(this, innerWidth);
|
|
1300
|
+
const rows = new Map<number, { sourceY: number; inset: number }>();
|
|
1301
|
+
const thoughtRows = new Set<number>();
|
|
1302
|
+
const output: string[] = [];
|
|
1303
|
+
const children = this.contentContainer.children;
|
|
1304
|
+
let sourceY = 0;
|
|
1305
|
+
let thoughtRun = 0;
|
|
1306
|
+
const previews: string[] = [];
|
|
1307
|
+
if (renderMode === "compact") {
|
|
1308
|
+
const message = this[originalMessageKey] ?? this.lastMessage;
|
|
1309
|
+
let run: string[] = [];
|
|
1310
|
+
const finishRun = () => {
|
|
1311
|
+
if (run.length) previews.push(run.join("\n\n").split(/\r\n|\r|\n/).find((line) => line.trim())?.trim() ?? "");
|
|
1312
|
+
run = [];
|
|
1238
1313
|
};
|
|
1239
|
-
|
|
1314
|
+
for (const item of message?.content ?? []) {
|
|
1315
|
+
if (item.type === "thinking") {
|
|
1316
|
+
if (item.thinking?.trim()) run.push(item.thinking.trim());
|
|
1317
|
+
} else finishRun();
|
|
1318
|
+
}
|
|
1319
|
+
finishRun();
|
|
1240
1320
|
}
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1321
|
+
for (let index = 0; index < children.length; index++) {
|
|
1322
|
+
const child = children[index];
|
|
1323
|
+
const childLines = child.render(innerWidth);
|
|
1324
|
+
const isThought = Boolean(child.onMouse && child.child);
|
|
1325
|
+
if (isThought || child instanceof Markdown && childLines.length) {
|
|
1326
|
+
let height = childLines.length;
|
|
1327
|
+
// Adjacent native text blocks belong to the same answer card. Keep
|
|
1328
|
+
// native paragraph order and source coordinates, not copied text.
|
|
1329
|
+
if (!isThought) {
|
|
1330
|
+
while (children[index + 1] instanceof Markdown) {
|
|
1331
|
+
height += children[++index].render(innerWidth).length;
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
const runIndex = thoughtRun;
|
|
1335
|
+
const hidden = isThought && (this.thinkingVisibilityOverrides.get(thoughtRun++) ?? this.hideThinkingBlock);
|
|
1336
|
+
const source = renderMode === "compact" && hidden
|
|
1337
|
+
? [defaultActivityTheme().fg("thinkingText", truncateToWidth(previews[runIndex] ?? "", innerWidth, "…"))]
|
|
1338
|
+
: nativeLines.slice(sourceY, sourceY + height);
|
|
1339
|
+
const role = isThought ? "mdLink" : "text";
|
|
1340
|
+
const baseLabel = isThought ? this.isStreaming ? "thinking" : "thought" : "Assistant";
|
|
1341
|
+
const label = isThought && renderMode === "compact" ? `${baseLabel} ${hidden ? "▸" : "▾"}` : baseLabel;
|
|
1342
|
+
const frame = frameNativeRows(label, source, width, defaultActivityTheme(), undefined, child, role, "", role);
|
|
1343
|
+
for (const [y, nativeY] of frame.sourceRows) {
|
|
1344
|
+
rows.set(output.length + y, { sourceY: sourceY + nativeY, inset: frame.inset });
|
|
1345
|
+
if (isThought) thoughtRows.add(output.length + y);
|
|
1346
|
+
}
|
|
1347
|
+
if (isThought && height) {
|
|
1348
|
+
rows.set(output.length, { sourceY, inset: frame.inset });
|
|
1349
|
+
thoughtRows.add(output.length);
|
|
1350
|
+
}
|
|
1351
|
+
output.push(...frame.lines);
|
|
1352
|
+
sourceY += height;
|
|
1353
|
+
} else {
|
|
1354
|
+
for (let y = 0; y < childLines.length; y++) rows.set(output.length + y, { sourceY: sourceY + y, inset: 0 });
|
|
1355
|
+
output.push(...nativeLines.slice(sourceY, sourceY + childLines.length));
|
|
1356
|
+
sourceY += childLines.length;
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
if (renderMode === "compact") {
|
|
1360
|
+
const terminal = assistantTerminalUpdate(this[originalMessageKey] ?? this.lastMessage);
|
|
1361
|
+
if (terminal) output.push(...renderPendingActivityUpdate(terminal, width, this));
|
|
1362
|
+
}
|
|
1363
|
+
panelAssistantViews.set(this, { rows, width: innerWidth, height: nativeLines.length });
|
|
1364
|
+
mouseClickHitTests.set(this, (event) => thoughtRows.has(event.y));
|
|
1365
|
+
return renderMode === "compact" && width < 8
|
|
1366
|
+
? output.map((line) => isNativeImageRow(line) ? line : truncateToWidth(line, Math.max(1, width), "")) : output;
|
|
1245
1367
|
};
|
|
1246
1368
|
|
|
1247
1369
|
const patchedUpdateContent = function (this: any, message: any, isStreaming = this.isStreaming) {
|
|
@@ -1250,8 +1372,13 @@ export default function prettyTui(pi: ExtensionAPI) {
|
|
|
1250
1372
|
this[renderedExpansionKey] = cleanToolsExpanded;
|
|
1251
1373
|
this.lastMessage = message;
|
|
1252
1374
|
if (renderMode !== "clean") {
|
|
1253
|
-
|
|
1375
|
+
this.hideThinkingBlock = renderMode === "full"
|
|
1376
|
+
? !(fullThinkingVisibility.get(this) ?? !fullThinkingHidden)
|
|
1377
|
+
: !(explicitThinkingVisibility.get(this) ?? !compactThinkingHidden);
|
|
1378
|
+
originalUpdateContent.call(this, renderMode === "compact" && assistantTerminalState(message)
|
|
1379
|
+
? { ...message, errorMessage: undefined, stopReason: "stop" } : message, isStreaming);
|
|
1254
1380
|
this.lastMessage = message;
|
|
1381
|
+
if (eligibleThinking(message)) thinkingComponents.set(assistantMessageKey(message), this);
|
|
1255
1382
|
return;
|
|
1256
1383
|
}
|
|
1257
1384
|
const groupedThinking = eligibleThinking(message);
|
|
@@ -1263,15 +1390,17 @@ export default function prettyTui(pi: ExtensionAPI) {
|
|
|
1263
1390
|
|
|
1264
1391
|
const previousHideThinkingBlock = this.hideThinkingBlock;
|
|
1265
1392
|
this.hideThinkingBlock = false;
|
|
1266
|
-
//
|
|
1267
|
-
// native content tree also removes the native MouseRegion that would
|
|
1393
|
+
// Shared thought presentation owns thinking completely. Removing it from
|
|
1394
|
+
// Pi's native content tree also removes the native MouseRegion that would
|
|
1268
1395
|
// otherwise toggle a second, unrelated `Thinking...` block.
|
|
1269
1396
|
const displayMessage =
|
|
1270
1397
|
groupedThinking && Array.isArray(message?.content)
|
|
1271
1398
|
? { ...message, content: message.content.filter((item: any) => item.type !== "thinking") }
|
|
1272
1399
|
: message;
|
|
1273
1400
|
try {
|
|
1274
|
-
originalUpdateContent.call(this,
|
|
1401
|
+
originalUpdateContent.call(this, assistantTerminalState(message)
|
|
1402
|
+
? { ...displayMessage, errorMessage: undefined, stopReason: "stop" }
|
|
1403
|
+
: displayMessage, isStreaming);
|
|
1275
1404
|
} finally {
|
|
1276
1405
|
this.hideThinkingBlock = previousHideThinkingBlock;
|
|
1277
1406
|
}
|
|
@@ -1286,14 +1415,17 @@ export default function prettyTui(pi: ExtensionAPI) {
|
|
|
1286
1415
|
) {
|
|
1287
1416
|
patchedUpdateContent.call(this, message, this.isStreaming);
|
|
1288
1417
|
}
|
|
1289
|
-
if (renderMode !== "clean") return
|
|
1418
|
+
if (renderMode !== "clean") return renderPanelAssistant.call(this, width);
|
|
1290
1419
|
const terminalMember = assistantTerminalUpdate(message);
|
|
1291
1420
|
if (!eligibleThinking(message)) {
|
|
1292
1421
|
if (!terminalMember) return originalRender.call(this, width);
|
|
1293
1422
|
const terminalGroup = activityTimeline.groupForMember(terminalMember.id);
|
|
1294
1423
|
if (!terminalGroup) return originalRender.call(this, width);
|
|
1295
1424
|
activityUpdateComponents.set(terminalMember.id, this);
|
|
1296
|
-
return
|
|
1425
|
+
return [
|
|
1426
|
+
...renderActivityUpdateProjection(terminalGroup, terminalMember, width, this),
|
|
1427
|
+
...(visibleAssistantText(message) ? originalRender.call(this, width) : []),
|
|
1428
|
+
];
|
|
1297
1429
|
}
|
|
1298
1430
|
|
|
1299
1431
|
const key = assistantMessageKey(message);
|
|
@@ -1311,10 +1443,9 @@ export default function prettyTui(pi: ExtensionAPI) {
|
|
|
1311
1443
|
}
|
|
1312
1444
|
const position = activityMemberPosition(group, member);
|
|
1313
1445
|
const visibleLines = visibleAssistantText(message) ? originalRender.call(this, width) : [];
|
|
1314
|
-
//
|
|
1315
|
-
//
|
|
1316
|
-
|
|
1317
|
-
const terminalLines = terminalMember && visibleLines.length === 0
|
|
1446
|
+
// Native errors are filtered above; append the shared alert exactly once
|
|
1447
|
+
// while preserving any partial visible answer outside the thought block.
|
|
1448
|
+
const terminalLines = terminalMember
|
|
1318
1449
|
? renderPendingActivityUpdate(terminalMember, width)
|
|
1319
1450
|
: [];
|
|
1320
1451
|
const appendTail = (activityLines: string[]) => {
|
|
@@ -1343,7 +1474,7 @@ export default function prettyTui(pi: ExtensionAPI) {
|
|
|
1343
1474
|
);
|
|
1344
1475
|
// Clean mode owns thought disclosure: revealing a group shows the thought
|
|
1345
1476
|
// member, while only a member click (or Ctrl+O) reveals its full text.
|
|
1346
|
-
const expanded =
|
|
1477
|
+
const expanded = expandedThinkingMembers.has(member.id) || (explicitThinkingVisibility.get(this) ?? cleanToolsExpanded);
|
|
1347
1478
|
const settledOwner = position.first ? activityGroupOwner(group) : undefined;
|
|
1348
1479
|
// A group without tools has no durable summary, so its own boundary is
|
|
1349
1480
|
// the signal that it finished.
|
|
@@ -1367,12 +1498,10 @@ export default function prettyTui(pi: ExtensionAPI) {
|
|
|
1367
1498
|
return appendTail(cached.lines);
|
|
1368
1499
|
}
|
|
1369
1500
|
const stateLabel = this.isStreaming ? "thinking" : "thought";
|
|
1370
|
-
const
|
|
1371
|
-
const header = groupTheme.fg("mdLink", "● ") +
|
|
1372
|
-
groupTheme.fg("text", groupTheme.bold(label));
|
|
1501
|
+
const header = thoughtPresentation.header(stateLabel, childWidth, groupTheme);
|
|
1373
1502
|
let contentLines: string[] = [header];
|
|
1374
1503
|
if (expanded) {
|
|
1375
|
-
contentLines.push(...
|
|
1504
|
+
contentLines.push(...thoughtPresentation.details(
|
|
1376
1505
|
this,
|
|
1377
1506
|
member.thinking ?? thinkingText(message),
|
|
1378
1507
|
groupTheme,
|
|
@@ -1405,7 +1534,15 @@ export default function prettyTui(pi: ExtensionAPI) {
|
|
|
1405
1534
|
|
|
1406
1535
|
const handleAssistantTranscriptMouse = function (this: any, event: any) {
|
|
1407
1536
|
const message = this[originalMessageKey] ?? this.lastMessage;
|
|
1408
|
-
if (renderMode !== "clean")
|
|
1537
|
+
if (renderMode !== "clean") {
|
|
1538
|
+
patchedRender.call(this, event.width);
|
|
1539
|
+
const view = panelAssistantViews.get(this);
|
|
1540
|
+
const row = view?.rows.get(event.y);
|
|
1541
|
+
if (!view || !row) return undefined;
|
|
1542
|
+
return originalHandleMouse.call(this, { ...event,
|
|
1543
|
+
x: Math.max(0, event.x - row.inset), y: row.sourceY, width: view.width, height: view.height,
|
|
1544
|
+
});
|
|
1545
|
+
}
|
|
1409
1546
|
if (!eligibleThinking(message)) {
|
|
1410
1547
|
const terminalMember = assistantTerminalUpdate(message);
|
|
1411
1548
|
const terminalGroup = terminalMember
|
|
@@ -1478,6 +1615,7 @@ export default function prettyTui(pi: ExtensionAPI) {
|
|
|
1478
1615
|
originalHandleMouse,
|
|
1479
1616
|
patchedHandleMouse,
|
|
1480
1617
|
};
|
|
1618
|
+
assistantPrototype.setHideThinkingBlock = patchedSetHideThinkingBlock;
|
|
1481
1619
|
assistantPrototype.updateContent = patchedUpdateContent;
|
|
1482
1620
|
assistantPrototype.render = patchedRender;
|
|
1483
1621
|
assistantPrototype.handleMouse = patchedHandleMouse;
|
|
@@ -1485,6 +1623,9 @@ export default function prettyTui(pi: ExtensionAPI) {
|
|
|
1485
1623
|
pi.on("session_shutdown", () => {
|
|
1486
1624
|
const patch = assistantPrototype[thinkingPatchKey];
|
|
1487
1625
|
if (!patch) return;
|
|
1626
|
+
if (assistantPrototype.setHideThinkingBlock === patchedSetHideThinkingBlock) {
|
|
1627
|
+
assistantPrototype.setHideThinkingBlock = originalSetHideThinkingBlock;
|
|
1628
|
+
}
|
|
1488
1629
|
if (patch.patchedUpdateContent === assistantPrototype.updateContent) {
|
|
1489
1630
|
assistantPrototype.updateContent = patch.originalUpdateContent;
|
|
1490
1631
|
}
|
|
@@ -1682,10 +1823,8 @@ export default function prettyTui(pi: ExtensionAPI) {
|
|
|
1682
1823
|
message: string,
|
|
1683
1824
|
type?: "info" | "warning" | "error",
|
|
1684
1825
|
) {
|
|
1685
|
-
if (renderMode !== "clean") {
|
|
1686
|
-
return originalShowExtensionNotify.call(this, message, type);
|
|
1687
|
-
}
|
|
1688
1826
|
const severity: ActivitySeverity = type ?? "info";
|
|
1827
|
+
if (renderMode === "full") return nativeExtensionNotify.call(this, message, severity);
|
|
1689
1828
|
if (!appendRuntimeActivityUpdate(this, message, severity)) {
|
|
1690
1829
|
nativeExtensionNotify.call(this, message, severity);
|
|
1691
1830
|
}
|
|
@@ -1693,6 +1832,13 @@ export default function prettyTui(pi: ExtensionAPI) {
|
|
|
1693
1832
|
|
|
1694
1833
|
const patchedToggleThinkingBlockVisibility = function (this: any) {
|
|
1695
1834
|
if (renderMode !== "clean") {
|
|
1835
|
+
if (renderMode === "full") {
|
|
1836
|
+
this.hideThinkingBlock = fullThinkingHidden;
|
|
1837
|
+
fullThinkingHidden = !fullThinkingHidden;
|
|
1838
|
+
} else {
|
|
1839
|
+
this.hideThinkingBlock = compactThinkingHidden;
|
|
1840
|
+
compactThinkingHidden = !compactThinkingHidden;
|
|
1841
|
+
}
|
|
1696
1842
|
return originalToggleThinkingBlockVisibility.call(this);
|
|
1697
1843
|
}
|
|
1698
1844
|
// Do not change Pi's persisted hideThinkingBlock setting in clean mode.
|
|
@@ -1704,7 +1850,7 @@ export default function prettyTui(pi: ExtensionAPI) {
|
|
|
1704
1850
|
};
|
|
1705
1851
|
|
|
1706
1852
|
const patchedShowStatus = function (this: any, message: string) {
|
|
1707
|
-
if (renderMode
|
|
1853
|
+
if (renderMode === "full") return originalShowStatus.call(this, message);
|
|
1708
1854
|
const [firstLine, ...remainingLines] = String(message).split("\n");
|
|
1709
1855
|
setLatestActivityStatus({
|
|
1710
1856
|
title: firstLine || "Status",
|
|
@@ -1715,13 +1861,15 @@ export default function prettyTui(pi: ExtensionAPI) {
|
|
|
1715
1861
|
};
|
|
1716
1862
|
|
|
1717
1863
|
const patchedShowWarning = function (this: any, message: string) {
|
|
1718
|
-
if (renderMode
|
|
1864
|
+
if (renderMode === "full") return originalShowWarning.call(this, message);
|
|
1865
|
+
if (!appendRuntimeActivityUpdate(this, message, "warning")) {
|
|
1719
1866
|
return originalShowWarning.call(this, message);
|
|
1720
1867
|
}
|
|
1721
1868
|
};
|
|
1722
1869
|
|
|
1723
1870
|
const patchedShowError = function (this: any, message: string) {
|
|
1724
|
-
if (renderMode
|
|
1871
|
+
if (renderMode === "full") return originalShowError.call(this, message);
|
|
1872
|
+
if (!appendRuntimeActivityUpdate(this, message, "error")) {
|
|
1725
1873
|
return originalShowError.call(this, message);
|
|
1726
1874
|
}
|
|
1727
1875
|
};
|
|
@@ -1772,9 +1920,7 @@ export default function prettyTui(pi: ExtensionAPI) {
|
|
|
1772
1920
|
}
|
|
1773
1921
|
// A mode switch can keep existing components rather than rebuild them.
|
|
1774
1922
|
const component: Component = {
|
|
1775
|
-
render: (width: number) =>
|
|
1776
|
-
? renderActivityUpdateProjection(group, member, width)
|
|
1777
|
-
: nativeNodes.flatMap((node) => node.render(width)),
|
|
1923
|
+
render: (width: number) => renderActivityUpdateProjection(group, member, width),
|
|
1778
1924
|
invalidate() {},
|
|
1779
1925
|
handleMouse: (event: any) => renderMode === "clean"
|
|
1780
1926
|
? handleActivityUpdateMouse(group, member, event)
|
|
@@ -1808,7 +1954,7 @@ export default function prettyTui(pi: ExtensionAPI) {
|
|
|
1808
1954
|
if (!group) continue;
|
|
1809
1955
|
mountActivityUpdate(
|
|
1810
1956
|
cacheNoticeHost.current, group, member, [],
|
|
1811
|
-
|
|
1957
|
+
toolComponents.get(toolCallId),
|
|
1812
1958
|
);
|
|
1813
1959
|
refreshActivityGroup(group);
|
|
1814
1960
|
cacheNoticeHost.current?.ui?.requestRender?.();
|
|
@@ -1817,7 +1963,7 @@ export default function prettyTui(pi: ExtensionAPI) {
|
|
|
1817
1963
|
flushDeferredCacheNotices = flushPendingCacheNotices;
|
|
1818
1964
|
|
|
1819
1965
|
const patchedAddCacheMissNotice = function (this: any, miss: any) {
|
|
1820
|
-
if (
|
|
1966
|
+
if (!this.chatContainer?.addChild) {
|
|
1821
1967
|
return originalAddCacheMissNotice.call(this, miss);
|
|
1822
1968
|
}
|
|
1823
1969
|
// Let Pi decide thresholds, formatting and cause labels. Capture its
|
|
@@ -1876,7 +2022,7 @@ export default function prettyTui(pi: ExtensionAPI) {
|
|
|
1876
2022
|
if (!group) return;
|
|
1877
2023
|
mountActivityUpdate(
|
|
1878
2024
|
this, group, member, nativeNodes,
|
|
1879
|
-
anchor?.toolCallId ?
|
|
2025
|
+
anchor?.toolCallId ? toolComponents.get(anchor.toolCallId) : undefined,
|
|
1880
2026
|
);
|
|
1881
2027
|
const component = activityUpdateComponents.get(member.id);
|
|
1882
2028
|
if (component && message) cacheNoticeOwners.set(component, message);
|
|
@@ -2520,10 +2666,11 @@ export default function prettyTui(pi: ExtensionAPI) {
|
|
|
2520
2666
|
prefix: () => {
|
|
2521
2667
|
const status = (state.compactToolStatus ?? "running") as ToolStatus;
|
|
2522
2668
|
const color = status === "success" ? "success" : status === "error" ? "error" : "dim";
|
|
2669
|
+
if (renderMode === "compact") return name === "bash" ? theme.fg("toolTitle", "$ ") : "";
|
|
2523
2670
|
return theme.fg(color, "● ");
|
|
2524
2671
|
},
|
|
2525
|
-
continuation: " ",
|
|
2526
|
-
content:
|
|
2672
|
+
continuation: renderMode === "compact" && name !== "bash" ? "" : " ",
|
|
2673
|
+
content: renderMode === "compact" ? theme.fg("muted", detail) :
|
|
2527
2674
|
theme.fg("text", theme.bold(name)) +
|
|
2528
2675
|
theme.fg("dim", "(") +
|
|
2529
2676
|
theme.fg("muted", detail) +
|
|
@@ -2769,65 +2916,6 @@ export default function prettyTui(pi: ExtensionAPI) {
|
|
|
2769
2916
|
settled: false,
|
|
2770
2917
|
};
|
|
2771
2918
|
|
|
2772
|
-
const toolLabel = (toolName: string, label?: string): string => label || toolName;
|
|
2773
|
-
|
|
2774
|
-
const conciseThirdPartyArgs = (args: any): string => {
|
|
2775
|
-
if (!args || typeof args !== "object" || Array.isArray(args)) return "";
|
|
2776
|
-
const sensitive = /(?:token|secret|password|authorization|credential|api[_-]?key)/iu;
|
|
2777
|
-
const preferred = ["path", "query", "url", "offset", "id"];
|
|
2778
|
-
const entries = Object.entries(args)
|
|
2779
|
-
.filter(([key, value]) => !sensitive.test(key) && ["string", "number", "boolean"].includes(typeof value))
|
|
2780
|
-
.sort(([left], [right]) => {
|
|
2781
|
-
const leftRank = preferred.indexOf(left);
|
|
2782
|
-
const rightRank = preferred.indexOf(right);
|
|
2783
|
-
return (leftRank < 0 ? preferred.length : leftRank) -
|
|
2784
|
-
(rightRank < 0 ? preferred.length : rightRank);
|
|
2785
|
-
})
|
|
2786
|
-
.slice(0, 2)
|
|
2787
|
-
.map(([key, value]) => {
|
|
2788
|
-
const safeValue = String(value)
|
|
2789
|
-
.replace(/(https?:\/\/)[^\s/@:]+:[^\s/@]+@/giu, "$1…:…@")
|
|
2790
|
-
.replace(/([?&](?:token|secret|password|api[_-]?key)=)[^&\s]+/giu, "$1…");
|
|
2791
|
-
const rendered = truncateToWidth(safeValue, 36, "…");
|
|
2792
|
-
return `${key}=${rendered}`;
|
|
2793
|
-
});
|
|
2794
|
-
return entries.join(" · ");
|
|
2795
|
-
};
|
|
2796
|
-
|
|
2797
|
-
const thirdPartyResultSummary = (component: any): string => {
|
|
2798
|
-
const detailsError = component.result?.details?.error;
|
|
2799
|
-
const firstText = component.result?.content?.find(
|
|
2800
|
-
(item: any) => item?.type === "text" && typeof item.text === "string" && item.text.length > 0,
|
|
2801
|
-
)?.text;
|
|
2802
|
-
const text = String(typeof detailsError === "string" ? detailsError : firstText ?? "");
|
|
2803
|
-
const boundedText = text.slice(0, 512);
|
|
2804
|
-
const newline = boundedText.indexOf("\n");
|
|
2805
|
-
const boundedFirstLine = boundedText.slice(0, newline < 0 ? boundedText.length : newline);
|
|
2806
|
-
const firstLine = stripTerminalSequences(boundedFirstLine).trim();
|
|
2807
|
-
if (firstLine) return truncateToWidth(firstLine, 72, "…");
|
|
2808
|
-
if (component.result) return component.result.isError ? "Failed" : "Done";
|
|
2809
|
-
return component.executionStarted ? "Running…" : "Pending";
|
|
2810
|
-
};
|
|
2811
|
-
|
|
2812
|
-
const renderThirdPartyCompact = (component: any, width: number, theme: any): string[] => {
|
|
2813
|
-
const label = toolLabel(component.toolName, component.toolDefinition?.label);
|
|
2814
|
-
const args = conciseThirdPartyArgs(component.args);
|
|
2815
|
-
const dotColor = component.result?.isError
|
|
2816
|
-
? "error"
|
|
2817
|
-
: component.result && !component.isPartial
|
|
2818
|
-
? "success"
|
|
2819
|
-
: "dim";
|
|
2820
|
-
const title = theme.fg(dotColor, "● ") +
|
|
2821
|
-
theme.fg("text", theme.bold(label)) +
|
|
2822
|
-
(args ? theme.fg("muted", `(${args})`) : "");
|
|
2823
|
-
const result = thirdPartyResultSummary(component);
|
|
2824
|
-
const resultColor = component.result?.isError ? "error" : "muted";
|
|
2825
|
-
return [
|
|
2826
|
-
truncateToWidth(title, Math.max(1, width), "…"),
|
|
2827
|
-
theme.fg(resultColor, truncateToWidth(` └ ${result}`, Math.max(1, width), "…")),
|
|
2828
|
-
];
|
|
2829
|
-
};
|
|
2830
|
-
|
|
2831
2919
|
const clearToolActivityHolds = () => {
|
|
2832
2920
|
for (const hold of toolActivityHolds.values()) {
|
|
2833
2921
|
if (hold.timer) clearTimeout(hold.timer);
|
|
@@ -2937,6 +3025,38 @@ export default function prettyTui(pi: ExtensionAPI) {
|
|
|
2937
3025
|
const originalToolHandleMouse = toolExecutionPrototype.handleMouse;
|
|
2938
3026
|
const renderedModeKey = Symbol("pretty-tui.tool-rendered-mode");
|
|
2939
3027
|
const cleanChildRenderCacheKey = Symbol("pretty-tui.clean-child-render-cache");
|
|
3028
|
+
const standaloneViews = new WeakMap<object, { projection: ToolProjection; offset: number; width: number }>();
|
|
3029
|
+
const projectTool = (owner: any, width: number, theme: any, mode: "compact" | "full") =>
|
|
3030
|
+
renderToolProjection(owner, width, theme, {
|
|
3031
|
+
mode, nested: nestedTools, nestedExpanded: Boolean(owner.expanded), panels: renderMode !== "clean",
|
|
3032
|
+
renderNative: (nativeWidth) => originalToolRender.call(owner, nativeWidth),
|
|
3033
|
+
});
|
|
3034
|
+
const handleProjectionMouse = (owner: any, projection: ToolProjection, event: any) => {
|
|
3035
|
+
const isClick = event.type === "click" && event.button === "left";
|
|
3036
|
+
const action = projection.actions.get(event.y);
|
|
3037
|
+
if (isClick && action) {
|
|
3038
|
+
nestedTools.toggle(action, Boolean(owner.expanded));
|
|
3039
|
+
owner.updateDisplay?.();
|
|
3040
|
+
owner.ui?.requestRender?.();
|
|
3041
|
+
return { handled: true };
|
|
3042
|
+
}
|
|
3043
|
+
if (isClick && projection.toggleRows.has(event.y)) {
|
|
3044
|
+
originalSetExpanded.call(owner, !owner.expanded);
|
|
3045
|
+
owner.ui?.requestRender?.();
|
|
3046
|
+
return { handled: true };
|
|
3047
|
+
}
|
|
3048
|
+
const native = projection.native;
|
|
3049
|
+
const sourceY = projection.sourceRows?.get(event.y);
|
|
3050
|
+
if (native && (projection.sourceRows ? sourceY !== undefined : event.y >= native.y && event.y < native.y + native.height)) {
|
|
3051
|
+
return originalToolHandleMouse.call(owner, {
|
|
3052
|
+
...event, x: Math.max(0, event.x - native.x),
|
|
3053
|
+
y: sourceY ?? event.y - native.y + native.sourceY,
|
|
3054
|
+
width: native.width, height: native.height + native.sourceY,
|
|
3055
|
+
});
|
|
3056
|
+
}
|
|
3057
|
+
// Projected rows must never be interpreted as native renderer rows.
|
|
3058
|
+
return isClick ? { handled: true } : undefined;
|
|
3059
|
+
};
|
|
2940
3060
|
const revealCleanGroup = revealActivityGroup;
|
|
2941
3061
|
const collapseCleanGroup = collapseActivityGroup;
|
|
2942
3062
|
const patchedMarkExecutionStarted = function (this: any) {
|
|
@@ -2972,7 +3092,7 @@ export default function prettyTui(pi: ExtensionAPI) {
|
|
|
2972
3092
|
return originalSetExpanded.call(this, expanded);
|
|
2973
3093
|
};
|
|
2974
3094
|
const patchedToolRender = function (this: any, width: number): string[] {
|
|
2975
|
-
|
|
3095
|
+
toolComponents.set(this.toolCallId, this);
|
|
2976
3096
|
knownToolCallIds.add(this.toolCallId);
|
|
2977
3097
|
const member = activityTimeline.addTool(this.toolCallId, this.toolName);
|
|
2978
3098
|
cleanToolNames.set(
|
|
@@ -2985,7 +3105,24 @@ export default function prettyTui(pi: ExtensionAPI) {
|
|
|
2985
3105
|
}
|
|
2986
3106
|
|
|
2987
3107
|
const group = activityTimeline.groupForMember(member.id);
|
|
2988
|
-
if (renderMode !== "clean" || !group)
|
|
3108
|
+
if (renderMode !== "clean" || !group) {
|
|
3109
|
+
const projection = renderMode !== "clean"
|
|
3110
|
+
? projectTool(this, width, defaultActivityTheme(), renderMode)
|
|
3111
|
+
: undefined;
|
|
3112
|
+
if (!projection) {
|
|
3113
|
+
standaloneViews.delete(this);
|
|
3114
|
+
mouseClickHitTests.delete(this);
|
|
3115
|
+
return originalToolRender.call(this, width);
|
|
3116
|
+
}
|
|
3117
|
+
const offset = renderMode === "full" ? 2 : 1;
|
|
3118
|
+
standaloneViews.set(this, { projection, offset, width });
|
|
3119
|
+
mouseClickHitTests.set(this, (event) => event.width === width && (
|
|
3120
|
+
projection.actions.has(event.y - offset) || projection.toggleRows.has(event.y - offset) ||
|
|
3121
|
+
Boolean(projection.sourceRows?.has(event.y - offset))
|
|
3122
|
+
));
|
|
3123
|
+
return [...Array<string>(offset).fill(""), ...projection.lines];
|
|
3124
|
+
}
|
|
3125
|
+
standaloneViews.delete(this);
|
|
2989
3126
|
const position = activityMemberPosition(group, member);
|
|
2990
3127
|
let renderedHeight = 0;
|
|
2991
3128
|
let nestedClickActions: Map<number, string> | undefined;
|
|
@@ -3019,8 +3156,6 @@ export default function prettyTui(pi: ExtensionAPI) {
|
|
|
3019
3156
|
// Other extensions (notably FFF in override mode) can register their own
|
|
3020
3157
|
// grep/find renderers under built-in names. Their default boxed shell is
|
|
3021
3158
|
// not one of our self-rendering specialized tools.
|
|
3022
|
-
const thirdParty = !SPECIALIZED_TOOL_NAMES.has(this.toolName) ||
|
|
3023
|
-
this.toolDefinition?.renderShell !== "self";
|
|
3024
3159
|
const cacheable = nestedTools.count(this.toolCallId) === 0 && !this.expanded && Boolean(this.result) && !this.isPartial;
|
|
3025
3160
|
const cached = cacheable ? this[cleanChildRenderCacheKey] : undefined;
|
|
3026
3161
|
let decoratedContent: string[];
|
|
@@ -3038,31 +3173,10 @@ export default function prettyTui(pi: ExtensionAPI) {
|
|
|
3038
3173
|
decoratedContent = cached.lines;
|
|
3039
3174
|
} else {
|
|
3040
3175
|
let contentLines: string[];
|
|
3041
|
-
|
|
3042
|
-
|
|
3176
|
+
const projection = projectTool(this, childWidth, childTheme, "compact");
|
|
3177
|
+
if (projection) {
|
|
3043
3178
|
contentLines = projection.lines;
|
|
3044
|
-
nestedClickActions = projection.actions;
|
|
3045
|
-
} else if (thirdParty) {
|
|
3046
|
-
const summaryLines = renderThirdPartyCompact(this, childWidth, childTheme);
|
|
3047
|
-
if (this.expanded) {
|
|
3048
|
-
const detailPrefix = " │ ";
|
|
3049
|
-
const nativeLines = originalToolRender.call(
|
|
3050
|
-
this,
|
|
3051
|
-
Math.max(1, childWidth - visibleWidth(detailPrefix)),
|
|
3052
|
-
);
|
|
3053
|
-
const nativeContentLines = nativeLines[0] === "" ? nativeLines.slice(1) : nativeLines;
|
|
3054
|
-
contentLines = [
|
|
3055
|
-
summaryLines[0],
|
|
3056
|
-
...nativeContentLines.map((line: string, index: number) =>
|
|
3057
|
-
childTheme.fg(
|
|
3058
|
-
"dim",
|
|
3059
|
-
index === nativeContentLines.length - 1 ? " └ " : detailPrefix,
|
|
3060
|
-
) + stripAnsiBackgrounds(line)
|
|
3061
|
-
),
|
|
3062
|
-
];
|
|
3063
|
-
} else {
|
|
3064
|
-
contentLines = summaryLines;
|
|
3065
|
-
}
|
|
3179
|
+
if (nestedTools.count(this.toolCallId) > 0) nestedClickActions = projection.actions;
|
|
3066
3180
|
} else {
|
|
3067
3181
|
const lines = originalToolRender.call(this, childWidth);
|
|
3068
3182
|
if (lines.length === 0) return lines;
|
|
@@ -3102,6 +3216,11 @@ export default function prettyTui(pi: ExtensionAPI) {
|
|
|
3102
3216
|
const group = member ? activityTimeline.groupForMember(member.id) : undefined;
|
|
3103
3217
|
const isLeftClick = event.type === "click" && event.button === "left";
|
|
3104
3218
|
if (renderMode !== "clean" || !member || !group) {
|
|
3219
|
+
patchedToolRender.call(this, event.width);
|
|
3220
|
+
const view = standaloneViews.get(this);
|
|
3221
|
+
if (view) return handleProjectionMouse(this, view.projection, {
|
|
3222
|
+
...event, y: event.y - view.offset, height: view.projection.lines.length,
|
|
3223
|
+
});
|
|
3105
3224
|
return originalToolHandleMouse.call(this, event);
|
|
3106
3225
|
}
|
|
3107
3226
|
const position = activityMemberPosition(group, member);
|
|
@@ -3128,33 +3247,15 @@ export default function prettyTui(pi: ExtensionAPI) {
|
|
|
3128
3247
|
return { handled: true };
|
|
3129
3248
|
}
|
|
3130
3249
|
|
|
3131
|
-
if (nestedTools.count(this.toolCallId) > 0
|
|
3250
|
+
if (nestedTools.count(this.toolCallId) > 0 || isThirdPartyTool(this)) {
|
|
3132
3251
|
const theme = cleanThemeForToolCall(this.toolCallId) ?? activityGroupTheme(group);
|
|
3133
|
-
const { childWidth } = activityTreeStyle(group, member, event.width, theme);
|
|
3134
|
-
const projection =
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
this.ui?.requestRender?.();
|
|
3141
|
-
}
|
|
3142
|
-
// The parent title is informational; only disclosure rows act on clicks.
|
|
3143
|
-
// Nested results are UI projections, not native renderer rows.
|
|
3144
|
-
return { handled: true };
|
|
3145
|
-
}
|
|
3146
|
-
|
|
3147
|
-
// Self-rendering tools reserve y=0 for Pi's leading spacer. Clean mode
|
|
3148
|
-
// replaces that spacer with the compact header for non-first members.
|
|
3149
|
-
const thirdPartyHeaderY = position.first ? summaryHeight + 1 : 0;
|
|
3150
|
-
if (
|
|
3151
|
-
(!SPECIALIZED_TOOL_NAMES.has(this.toolName) || this.toolDefinition?.renderShell !== "self") &&
|
|
3152
|
-
isLeftClick &&
|
|
3153
|
-
event.y === thirdPartyHeaderY
|
|
3154
|
-
) {
|
|
3155
|
-
originalSetExpanded.call(this, !this.expanded);
|
|
3156
|
-
this.ui?.requestRender?.();
|
|
3157
|
-
return { handled: true };
|
|
3252
|
+
const { childWidth, prefixWidth } = activityTreeStyle(group, member, event.width, theme);
|
|
3253
|
+
const projection = projectTool(this, childWidth, theme, "compact");
|
|
3254
|
+
if (projection) return handleProjectionMouse(this, projection, {
|
|
3255
|
+
...event, x: Math.max(0, event.x - prefixWidth),
|
|
3256
|
+
y: event.y - (position.first ? summaryHeight + 1 : 0),
|
|
3257
|
+
width: childWidth, height: projection.lines.length,
|
|
3258
|
+
});
|
|
3158
3259
|
}
|
|
3159
3260
|
|
|
3160
3261
|
if (!this.result && isLeftClick) {
|
|
@@ -3653,7 +3754,7 @@ export default function prettyTui(pi: ExtensionAPI) {
|
|
|
3653
3754
|
cleanCompactToolCallIds.clear();
|
|
3654
3755
|
cleanGroupToolCallIds.clear();
|
|
3655
3756
|
cleanToolCallGroupOwners.clear();
|
|
3656
|
-
|
|
3757
|
+
toolComponents.clear();
|
|
3657
3758
|
cleanToolThemes.clear();
|
|
3658
3759
|
cleanToolNames.clear();
|
|
3659
3760
|
clearToolActivityHolds();
|
|
@@ -3902,7 +4003,11 @@ export default function prettyTui(pi: ExtensionAPI) {
|
|
|
3902
4003
|
finishGroup();
|
|
3903
4004
|
};
|
|
3904
4005
|
|
|
3905
|
-
pi.on("session_start", (_event, ctx) =>
|
|
4006
|
+
pi.on("session_start", (_event, ctx) => {
|
|
4007
|
+
fullThinkingHidden = false;
|
|
4008
|
+
compactThinkingHidden = true;
|
|
4009
|
+
restoreCleanSession(ctx);
|
|
4010
|
+
});
|
|
3906
4011
|
pi.on("session_tree", (_event, ctx) => restoreCleanSession(ctx));
|
|
3907
4012
|
pi.on("session_before_compact", () => {
|
|
3908
4013
|
// Pi emits this for manual, threshold, and overflow-recovery compaction.
|
|
@@ -4071,7 +4176,7 @@ export default function prettyTui(pi: ExtensionAPI) {
|
|
|
4071
4176
|
cleanRun.activeToolCallId = undefined;
|
|
4072
4177
|
cleanRun.activeToolName = undefined;
|
|
4073
4178
|
cleanRun.activity = "thinking";
|
|
4074
|
-
|
|
4179
|
+
startWorkingElapsedTimer();
|
|
4075
4180
|
});
|
|
4076
4181
|
pi.on("tool_execution_start", (event) => {
|
|
4077
4182
|
if ((event as any).parentToolCallId) {
|
|
@@ -4222,9 +4327,33 @@ export default function prettyTui(pi: ExtensionAPI) {
|
|
|
4222
4327
|
});
|
|
4223
4328
|
});
|
|
4224
4329
|
|
|
4330
|
+
const nativeDefinitions: Record<string, any> = {
|
|
4331
|
+
read: createReadToolDefinition(cwd), bash: createBashToolDefinition(cwd),
|
|
4332
|
+
edit: createEditToolDefinition(cwd), write: createWriteToolDefinition(cwd),
|
|
4333
|
+
grep: createGrepToolDefinition(cwd), find: createFindToolDefinition(cwd), ls: createLsToolDefinition(cwd),
|
|
4334
|
+
};
|
|
4335
|
+
const registerStyledTool = (definition: any) => {
|
|
4336
|
+
const native = nativeDefinitions[definition.name];
|
|
4337
|
+
const select = (kind: "renderCall" | "renderResult", args: any[]) => {
|
|
4338
|
+
const context = args.at(-1);
|
|
4339
|
+
const useNative = renderMode === "full" || renderMode === "compact" && context.expanded;
|
|
4340
|
+
const namespace = useNative ? "nativeFull" : "pretty";
|
|
4341
|
+
const state = context.state[namespace] ??= { state: {} };
|
|
4342
|
+
const scoped = { ...context, state: state.state, lastComponent: state[kind] };
|
|
4343
|
+
const renderer = useNative ? native[kind] : definition[kind];
|
|
4344
|
+
const component = renderer?.(...args.slice(0, -1), scoped);
|
|
4345
|
+
state[kind] = component;
|
|
4346
|
+
return component;
|
|
4347
|
+
};
|
|
4348
|
+
pi.registerTool({ ...definition,
|
|
4349
|
+
renderCall: (...args: any[]) => select("renderCall", args),
|
|
4350
|
+
renderResult: (...args: any[]) => select("renderResult", args),
|
|
4351
|
+
});
|
|
4352
|
+
};
|
|
4353
|
+
|
|
4225
4354
|
const read = createReadTool(cwd);
|
|
4226
4355
|
const readLabel = read.label || read.name;
|
|
4227
|
-
|
|
4356
|
+
registerStyledTool({
|
|
4228
4357
|
...read,
|
|
4229
4358
|
renderShell: "self",
|
|
4230
4359
|
renderCall(args: any, theme: any, context: any) {
|
|
@@ -4253,7 +4382,7 @@ export default function prettyTui(pi: ExtensionAPI) {
|
|
|
4253
4382
|
|
|
4254
4383
|
const bash = createBashTool(cwd);
|
|
4255
4384
|
const bashLabel = bash.label || bash.name;
|
|
4256
|
-
|
|
4385
|
+
registerStyledTool({
|
|
4257
4386
|
...bash,
|
|
4258
4387
|
renderShell: "self",
|
|
4259
4388
|
renderCall(args: any, theme: any, context: any) {
|
|
@@ -4304,7 +4433,7 @@ export default function prettyTui(pi: ExtensionAPI) {
|
|
|
4304
4433
|
|
|
4305
4434
|
const edit = createEditTool(cwd);
|
|
4306
4435
|
const editLabel = edit.label || edit.name;
|
|
4307
|
-
|
|
4436
|
+
registerStyledTool({
|
|
4308
4437
|
...edit,
|
|
4309
4438
|
renderShell: "self",
|
|
4310
4439
|
renderCall(args: any, theme: any, context: any) {
|
|
@@ -4347,7 +4476,7 @@ export default function prettyTui(pi: ExtensionAPI) {
|
|
|
4347
4476
|
|
|
4348
4477
|
const write = createWriteTool(cwd);
|
|
4349
4478
|
const writeLabel = write.label || write.name;
|
|
4350
|
-
|
|
4479
|
+
registerStyledTool({
|
|
4351
4480
|
...write,
|
|
4352
4481
|
renderShell: "self",
|
|
4353
4482
|
renderCall(args: any, theme: any, context: any) {
|
|
@@ -4379,7 +4508,7 @@ export default function prettyTui(pi: ExtensionAPI) {
|
|
|
4379
4508
|
|
|
4380
4509
|
const grep = createGrepTool(cwd);
|
|
4381
4510
|
const grepLabel = grep.label || grep.name;
|
|
4382
|
-
|
|
4511
|
+
registerStyledTool({
|
|
4383
4512
|
...grep,
|
|
4384
4513
|
renderShell: "self",
|
|
4385
4514
|
renderCall(args: any, theme: any, context: any) {
|
|
@@ -4400,7 +4529,7 @@ export default function prettyTui(pi: ExtensionAPI) {
|
|
|
4400
4529
|
|
|
4401
4530
|
const find = createFindTool(cwd);
|
|
4402
4531
|
const findLabel = find.label || find.name;
|
|
4403
|
-
|
|
4532
|
+
registerStyledTool({
|
|
4404
4533
|
...find,
|
|
4405
4534
|
renderShell: "self",
|
|
4406
4535
|
renderCall(args: any, theme: any, context: any) {
|
|
@@ -4419,7 +4548,7 @@ export default function prettyTui(pi: ExtensionAPI) {
|
|
|
4419
4548
|
|
|
4420
4549
|
const ls = createLsTool(cwd);
|
|
4421
4550
|
const lsLabel = ls.label || ls.name;
|
|
4422
|
-
|
|
4551
|
+
registerStyledTool({
|
|
4423
4552
|
...ls,
|
|
4424
4553
|
renderShell: "self",
|
|
4425
4554
|
renderCall(args: any, theme: any, context: any) {
|