pi-tui-footer 1.2.7 → 1.2.9
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 +2 -2
- package/extensions/rounded-tools.ts +129 -165
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -45,9 +45,9 @@ pi update --extensions
|
|
|
45
45
|
|
|
46
46
|
### 圆角工具框与第三方工具
|
|
47
47
|
|
|
48
|
-
`roundedTools`
|
|
48
|
+
`roundedTools` 会为 Pi 内置工具(read、write、edit、bash、powershell、grep、find、ls)添加圆角边框。第三方扩展注册的自定义工具不会自动添加圆角边框。
|
|
49
49
|
|
|
50
|
-
|
|
50
|
+
若第三方扩展会覆盖同名内置工具,请在 `~/.pi/agent/settings.json` 的 `packages` 中将 `pi-tui-footer` 放在该扩展之后。Pi 按扩展加载顺序处理同名工具,先注册的定义生效;此顺序可保留第三方工具的执行实现:
|
|
51
51
|
|
|
52
52
|
```json
|
|
53
53
|
{
|
|
@@ -1,27 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Rounded tool frames
|
|
2
|
+
* Rounded tool frames — rounded frames for tool calls and results.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* localized copy of npm:pi-rounded-tools@0.1.3, MIT, by OrionPax,
|
|
6
|
-
* https://github.com/orionpax1997/pi-rounded-tools), integrated into pi-tui
|
|
7
|
-
* (based on https://github.com/OldSuns/pi-open-tui v0.3.11, MIT, by
|
|
8
|
-
* OldSun) and gated behind `roundedTools` in pi-tui config
|
|
9
|
-
* (~/.pi/agent/pi-tui.json, default on).
|
|
10
|
-
*
|
|
11
|
-
* Re-registers the built-in tools (read, write, edit, bash, grep, find, ls)
|
|
4
|
+
* Re-registers built-in tools (read, write, bash, powershell, grep, find, ls)
|
|
12
5
|
* with `renderShell: "self"` and wraps each tool call / result in a frame
|
|
13
6
|
* drawn with Unicode rounded-corner characters (╭ ╮ ╰ ╯ ─ │).
|
|
14
7
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
8
|
+
* Tools that already render their own shell (e.g. `edit` in modern Pi versions)
|
|
9
|
+
* are preserved as-is to avoid duplicate nesting and UI glitches.
|
|
17
10
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* component in a `RoundedFrame`. That way bash's preview, read's syntax
|
|
21
|
-
* highlighting, edit's diff stats, etc. all stay exactly as pi ships them.
|
|
11
|
+
* Border color follows `theme.fg("border", ...)` so it adapts to your theme,
|
|
12
|
+
* using warning (yellow) during pending/streaming execution and error (red) on failure.
|
|
22
13
|
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
14
|
+
* Non-default tools (grep, find, ls, powershell) preserve `defaultActive: false`
|
|
15
|
+
* to avoid polluting the model's active toolset.
|
|
25
16
|
*/
|
|
26
17
|
|
|
27
18
|
import type { ExtensionAPI, ToolDefinition } from "@earendil-works/pi-coding-agent";
|
|
@@ -41,23 +32,55 @@ import { visibleWidth } from "@earendil-works/pi-tui";
|
|
|
41
32
|
// ─── Rounded-corner frame ────────────────────────────────────────────────
|
|
42
33
|
//
|
|
43
34
|
// Three modes let call + result stack into one continuous frame:
|
|
44
|
-
// - "closed" → ╭ ── ╮ / │ x │ / ╰ ── ╯ (standalone box)
|
|
35
|
+
// - "closed" → ╭ ── ╮ / │ x │ / ╰ ── ╯ (standalone box / pending call)
|
|
45
36
|
// - "open-bottom" → ╭ ── ╮ / │ x │ (top of a stacked frame)
|
|
46
37
|
// - "open-top" → │ x │ / ╰ ── ╯ (bottom of a stacked frame)
|
|
47
38
|
|
|
48
|
-
type FrameMode = "closed" | "open-bottom" | "open-top";
|
|
39
|
+
export type FrameMode = "closed" | "open-bottom" | "open-top";
|
|
40
|
+
|
|
41
|
+
export class RoundedFrame implements Component {
|
|
42
|
+
private cachedWidth: number | undefined;
|
|
43
|
+
private cachedInnerLines: string[] | undefined;
|
|
44
|
+
private cachedLines: string[] | undefined;
|
|
49
45
|
|
|
50
|
-
class RoundedFrame implements Component {
|
|
51
46
|
constructor(
|
|
52
|
-
private
|
|
53
|
-
private
|
|
54
|
-
private
|
|
47
|
+
private inner: Component,
|
|
48
|
+
private border: (text: string) => string,
|
|
49
|
+
private mode: FrameMode = "closed",
|
|
50
|
+
private colorKey: string = "border",
|
|
55
51
|
) {}
|
|
56
52
|
|
|
57
53
|
getInner(): Component {
|
|
58
54
|
return this.inner;
|
|
59
55
|
}
|
|
60
56
|
|
|
57
|
+
getMode(): FrameMode {
|
|
58
|
+
return this.mode;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
setMode(mode: FrameMode): void {
|
|
62
|
+
if (this.mode !== mode) {
|
|
63
|
+
this.mode = mode;
|
|
64
|
+
this.invalidate();
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
update(
|
|
69
|
+
inner: Component,
|
|
70
|
+
border: (text: string) => string,
|
|
71
|
+
mode: FrameMode,
|
|
72
|
+
colorKey: string,
|
|
73
|
+
): this {
|
|
74
|
+
if (this.inner !== inner || this.mode !== mode || this.colorKey !== colorKey) {
|
|
75
|
+
this.inner = inner;
|
|
76
|
+
this.mode = mode;
|
|
77
|
+
this.colorKey = colorKey;
|
|
78
|
+
this.invalidate();
|
|
79
|
+
}
|
|
80
|
+
this.border = border;
|
|
81
|
+
return this;
|
|
82
|
+
}
|
|
83
|
+
|
|
61
84
|
invalidate(): void {
|
|
62
85
|
this.inner.invalidate?.();
|
|
63
86
|
this.cachedWidth = undefined;
|
|
@@ -91,17 +114,6 @@ class RoundedFrame implements Component {
|
|
|
91
114
|
});
|
|
92
115
|
}
|
|
93
116
|
|
|
94
|
-
// pi's built-in tool renderers cache the previously-returned component
|
|
95
|
-
// via `context.lastComponent` and call mutating methods on it
|
|
96
|
-
// (`Text.setText`, `Container.clear`/`addChild`, `invalidate`) so they
|
|
97
|
-
// can re-render in place during streaming. Once we wrap the inner in a
|
|
98
|
-
// RoundedFrame, pi hands our wrapper back to the inner renderer on the
|
|
99
|
-
// next render — but pi's try/catch around `callRenderer(...)` falls
|
|
100
|
-
// back to a plain `createCallFallback()` text node if the inner
|
|
101
|
-
// renderer throws, which would make the border vanish on every
|
|
102
|
-
// Ctrl+O toggle. Forward these methods to the inner so the original
|
|
103
|
-
// renderers work unchanged; calls on a wrapper whose inner lacks the
|
|
104
|
-
// method are no-ops thanks to optional chaining.
|
|
105
117
|
setText(text: string): void {
|
|
106
118
|
(this.inner as any).setText?.(text);
|
|
107
119
|
}
|
|
@@ -122,21 +134,6 @@ class RoundedFrame implements Component {
|
|
|
122
134
|
const innerWidth = Math.max(1, width - 4); // │ + space + content + space + │
|
|
123
135
|
const innerLines = this.inner.render(innerWidth);
|
|
124
136
|
|
|
125
|
-
// pi's TUI re-renders every transcript component on every render
|
|
126
|
-
// cycle (each keystroke), but built-in components below us (e.g.
|
|
127
|
-
// `Text`, `Box`) cache their rendered line arrays and return the
|
|
128
|
-
// same array reference when content and width haven't changed.
|
|
129
|
-
// Without a cache here, every keystroke re-runs `visibleWidth()`
|
|
130
|
-
// (ANSI parsing) and string building for every line of every
|
|
131
|
-
// framed tool entry — O(N_session) per keypress, visible as input
|
|
132
|
-
// lag on long sessions.
|
|
133
|
-
//
|
|
134
|
-
// Cache key: `width` plus the array reference returned by
|
|
135
|
-
// `inner.render()`. Built-in Text/Box return the same reference on
|
|
136
|
-
// cache hits and a fresh array whenever their content changes
|
|
137
|
-
// (e.g. Ctrl+O expand), so reference equality is a reliable change
|
|
138
|
-
// signal. `invalidate()` (theme changes etc.) clears the cache
|
|
139
|
-
// explicitly, and so does an actual `width` change.
|
|
140
137
|
if (
|
|
141
138
|
this.cachedLines &&
|
|
142
139
|
this.cachedWidth === width &&
|
|
@@ -161,10 +158,6 @@ class RoundedFrame implements Component {
|
|
|
161
158
|
out.push(side + " " + line + pad + " " + side);
|
|
162
159
|
}
|
|
163
160
|
} else if (this.mode === "closed") {
|
|
164
|
-
// Standalone closed frame with no content: draw a placeholder row
|
|
165
|
-
// so the box still has visual presence. Skipped for open-top /
|
|
166
|
-
// open-bottom since they pair with another half-frame and a fake
|
|
167
|
-
// empty row would show up as a stray blank line in the middle.
|
|
168
161
|
out.push(side + " ".repeat(width - 2) + side);
|
|
169
162
|
}
|
|
170
163
|
|
|
@@ -176,10 +169,6 @@ class RoundedFrame implements Component {
|
|
|
176
169
|
this.cachedLines = out;
|
|
177
170
|
return out;
|
|
178
171
|
}
|
|
179
|
-
|
|
180
|
-
private cachedWidth: number | undefined;
|
|
181
|
-
private cachedInnerLines: string[] | undefined;
|
|
182
|
-
private cachedLines: string[] | undefined;
|
|
183
172
|
}
|
|
184
173
|
|
|
185
174
|
const frame = (
|
|
@@ -187,24 +176,8 @@ const frame = (
|
|
|
187
176
|
theme: { fg: (color: string, text: string) => string },
|
|
188
177
|
mode: FrameMode = "closed",
|
|
189
178
|
colorKey: string = "border",
|
|
190
|
-
):
|
|
179
|
+
): RoundedFrame => new RoundedFrame(inner, (t) => theme.fg(colorKey, t), mode, colorKey);
|
|
191
180
|
|
|
192
|
-
/**
|
|
193
|
-
* Pick a border color key based on the tool's runtime state.
|
|
194
|
-
*
|
|
195
|
-
* - still running (`context.isPartial`) → warning (yellow)
|
|
196
|
-
* - finished with an error (`context.isError`) → error (red)
|
|
197
|
-
* - finished successfully → border (theme default)
|
|
198
|
-
*
|
|
199
|
-
* Both `renderCall` and `renderResult` go through this picker, so when
|
|
200
|
-
* they stack into one continuous frame, the borders match. pi re-renders
|
|
201
|
-
* the whole component on every state transition (args streaming → done,
|
|
202
|
-
* partial → final), and `context.isPartial` flips accordingly, so call
|
|
203
|
-
* and result always pick the same color at any given moment.
|
|
204
|
-
*
|
|
205
|
-
* Mirrors pi's own 3-state scheme (`toolPendingBg` / `toolSuccessBg` /
|
|
206
|
-
* `toolErrorBg`) — see `theme.js`.
|
|
207
|
-
*/
|
|
208
181
|
function borderColorFor(
|
|
209
182
|
context: { isPartial?: boolean; isError?: boolean } | undefined,
|
|
210
183
|
): string {
|
|
@@ -219,11 +192,6 @@ type ToolDef = ToolDefinition<any, any, any>;
|
|
|
219
192
|
|
|
220
193
|
const EMPTY_COMPONENT: Component = { render: () => [], invalidate: () => {} };
|
|
221
194
|
|
|
222
|
-
/**
|
|
223
|
-
* Unwrap inner component from a RoundedFrame if context.lastComponent is wrapped,
|
|
224
|
-
* allowing underlying renderers (e.g. edit, bash, read) to preserve their component
|
|
225
|
-
* cache across renders instead of recreating on every tick.
|
|
226
|
-
*/
|
|
227
195
|
function unwrapInner(component: unknown): Component | undefined {
|
|
228
196
|
if (component instanceof RoundedFrame) {
|
|
229
197
|
return component.getInner();
|
|
@@ -231,15 +199,12 @@ function unwrapInner(component: unknown): Component | undefined {
|
|
|
231
199
|
return component as Component | undefined;
|
|
232
200
|
}
|
|
233
201
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
*/
|
|
241
|
-
function wrapBuiltin(def: ToolDef) {
|
|
242
|
-
const callMode: FrameMode = def.renderResult ? "open-bottom" : "closed";
|
|
202
|
+
export function wrapBuiltin(def: ToolDef): ToolDef {
|
|
203
|
+
// Tools that already manage their own shell (e.g. edit) should remain untouched.
|
|
204
|
+
if (def.renderShell === "self") {
|
|
205
|
+
return def;
|
|
206
|
+
}
|
|
207
|
+
|
|
243
208
|
return {
|
|
244
209
|
...def,
|
|
245
210
|
renderShell: "self" as const,
|
|
@@ -248,38 +213,87 @@ function wrapBuiltin(def: ToolDef) {
|
|
|
248
213
|
const inner: Component = def.renderCall
|
|
249
214
|
? def.renderCall(args, theme, { ...context, lastComponent: unwrapped })
|
|
250
215
|
: EMPTY_COMPONENT;
|
|
251
|
-
|
|
216
|
+
|
|
217
|
+
// If the tool is still pending/streaming and no result has arrived yet,
|
|
218
|
+
// close the bottom border so it renders as a complete box instead of an open frame.
|
|
219
|
+
const isPendingWithoutResult = Boolean(context?.isPartial && !context?.state?.__hasResult);
|
|
220
|
+
const mode: FrameMode = def.renderResult && !isPendingWithoutResult ? "open-bottom" : "closed";
|
|
221
|
+
const borderFn = (t: string) => theme.fg(borderColorFor(context), t);
|
|
222
|
+
|
|
223
|
+
let callFrame: RoundedFrame;
|
|
224
|
+
if (context?.lastComponent instanceof RoundedFrame) {
|
|
225
|
+
callFrame = context.lastComponent.update(inner, borderFn, mode, borderColorFor(context));
|
|
226
|
+
} else {
|
|
227
|
+
callFrame = frame(inner, theme, mode, borderColorFor(context));
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (context?.state) {
|
|
231
|
+
context.state.__callFrame = callFrame;
|
|
232
|
+
}
|
|
233
|
+
return callFrame;
|
|
252
234
|
},
|
|
253
235
|
renderResult: (result: any, options: any, theme: any, context: any) => {
|
|
236
|
+
if (context?.state) {
|
|
237
|
+
context.state.__hasResult = true;
|
|
238
|
+
// Connect seamlessly with the call frame by opening its bottom border.
|
|
239
|
+
const callFrame = context.state.__callFrame;
|
|
240
|
+
if (callFrame instanceof RoundedFrame && callFrame.getMode() !== "open-bottom") {
|
|
241
|
+
callFrame.setMode("open-bottom");
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
254
245
|
const unwrapped = unwrapInner(context?.lastComponent);
|
|
255
246
|
const inner: Component = def.renderResult
|
|
256
247
|
? def.renderResult(result, options, theme, { ...context, lastComponent: unwrapped })
|
|
257
248
|
: EMPTY_COMPONENT;
|
|
249
|
+
const borderFn = (t: string) => theme.fg(borderColorFor(context), t);
|
|
250
|
+
|
|
251
|
+
if (context?.lastComponent instanceof RoundedFrame) {
|
|
252
|
+
return context.lastComponent.update(inner, borderFn, "open-top", borderColorFor(context));
|
|
253
|
+
}
|
|
258
254
|
return frame(inner, theme, "open-top", borderColorFor(context));
|
|
259
255
|
},
|
|
260
256
|
};
|
|
261
257
|
}
|
|
262
258
|
|
|
263
|
-
/**
|
|
264
|
-
* Register built-in tools (read, write, edit, bash, powershell, grep, find, ls),
|
|
265
|
-
* either with rounded frames (`enabled`) or with stock definitions (`!enabled`).
|
|
266
|
-
*
|
|
267
|
-
* If a third-party extension has already replaced a built-in tool (e.g. pi-fff
|
|
268
|
-
* overriding grep/find), that tool is skipped here to prevent clobbering.
|
|
269
|
-
*/
|
|
270
259
|
export function registerRoundedTools(
|
|
271
260
|
target: ExtensionAPI | ((tool: ToolDef) => void),
|
|
272
261
|
enabled: boolean,
|
|
273
262
|
cwd: string,
|
|
274
|
-
|
|
263
|
+
_captured?: Map<string, ToolDef>,
|
|
275
264
|
): void {
|
|
276
265
|
const register = typeof target === "function" ? target : target.registerTool.bind(target);
|
|
277
266
|
|
|
267
|
+
let settings: any;
|
|
268
|
+
if (typeof target !== "function" && target.getSettings) {
|
|
269
|
+
try {
|
|
270
|
+
settings = target.getSettings();
|
|
271
|
+
} catch {
|
|
272
|
+
// runtime not initialized yet during early load
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
const autoResizeImages = settings?.images?.autoResize ?? true;
|
|
276
|
+
const shellCommandPrefix = settings?.shellCommandPrefix;
|
|
277
|
+
const shellPath = settings?.shellPath;
|
|
278
|
+
|
|
279
|
+
let activeToolNames: Set<string> | undefined;
|
|
280
|
+
if (typeof target !== "function" && target.getActiveTools) {
|
|
281
|
+
try {
|
|
282
|
+
const active = target.getActiveTools();
|
|
283
|
+
if (Array.isArray(active)) {
|
|
284
|
+
activeToolNames = new Set(active);
|
|
285
|
+
}
|
|
286
|
+
} catch {
|
|
287
|
+
// runtime not initialized yet during early load
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
const defaultActiveNames = activeToolNames ?? new Set(["read", "bash", "edit", "write"]);
|
|
291
|
+
|
|
278
292
|
const builtins: Record<string, () => ToolDef> = {
|
|
279
|
-
read: () => createReadToolDefinition(cwd),
|
|
293
|
+
read: () => createReadToolDefinition(cwd, { autoResizeImages }),
|
|
280
294
|
write: () => createWriteToolDefinition(cwd),
|
|
281
295
|
edit: () => createEditToolDefinition(cwd),
|
|
282
|
-
bash: () => createBashToolDefinition(cwd),
|
|
296
|
+
bash: () => createBashToolDefinition(cwd, { commandPrefix: shellCommandPrefix, shellPath }),
|
|
283
297
|
powershell: () => createPowerShellToolDefinition(cwd),
|
|
284
298
|
ls: () => createLsToolDefinition(cwd),
|
|
285
299
|
grep: () => createGrepToolDefinition(cwd),
|
|
@@ -287,86 +301,36 @@ export function registerRoundedTools(
|
|
|
287
301
|
};
|
|
288
302
|
|
|
289
303
|
for (const [name, factory] of Object.entries(builtins)) {
|
|
290
|
-
|
|
291
|
-
|
|
304
|
+
const rawDef = factory();
|
|
305
|
+
// Skip tools that already manage their own shell framing (e.g. edit)
|
|
306
|
+
if (rawDef.renderShell === "self") {
|
|
307
|
+
continue;
|
|
292
308
|
}
|
|
309
|
+
|
|
310
|
+
const shouldBeActive = defaultActiveNames.has(name);
|
|
311
|
+
const baseDef: ToolDef = {
|
|
312
|
+
...rawDef,
|
|
313
|
+
defaultActive: shouldBeActive,
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
register(enabled ? wrapBuiltin(baseDef) : baseDef);
|
|
293
317
|
}
|
|
294
318
|
}
|
|
295
319
|
|
|
296
|
-
/**
|
|
297
|
-
* Manager class encapsulating rounded tools registration and lifecycle updates.
|
|
298
|
-
*
|
|
299
|
-
* In addition to wrapping built-in tools, it installs a `pi.registerTool` proxy
|
|
300
|
-
* so that any tool registered by third-party extensions (e.g. pi-fff's ffgrep,
|
|
301
|
-
* fffind, fff-multi-grep, or overridden grep/find) automatically receives rounded
|
|
302
|
-
* frames when `roundedTools` is enabled, without maintaining a fragile hardcoded
|
|
303
|
-
* name whitelist.
|
|
304
|
-
*
|
|
305
|
-
* Load-order note: For extensions that register tools at load time, place
|
|
306
|
-
* `pi-tui-footer` before those packages in `~/.pi/agent/settings.json` so the
|
|
307
|
-
* proxy is active when they load.
|
|
308
|
-
*/
|
|
309
320
|
export class RoundedToolsManager {
|
|
310
321
|
private enabled = false;
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
private originalRegisterTool!: (tool: ToolDef) => void;
|
|
314
|
-
/** Original (unwrapped) definitions captured from third-party tools. */
|
|
315
|
-
private readonly captured = new Map<string, ToolDef>();
|
|
316
|
-
|
|
317
|
-
constructor(private readonly pi: ExtensionAPI) {
|
|
318
|
-
this.installProxy();
|
|
319
|
-
}
|
|
322
|
+
|
|
323
|
+
constructor(private readonly pi: ExtensionAPI) {}
|
|
320
324
|
|
|
321
325
|
/** Register initial tools before session_start with fallback cwd. */
|
|
322
326
|
init(enabled: boolean, cwd = process.cwd()): void {
|
|
323
327
|
this.enabled = enabled;
|
|
324
|
-
this.
|
|
325
|
-
registerRoundedTools(this.originalRegisterTool, enabled, cwd, this.captured);
|
|
326
|
-
this.reRegisterCaptured();
|
|
328
|
+
registerRoundedTools(this.pi, enabled, cwd);
|
|
327
329
|
}
|
|
328
330
|
|
|
329
331
|
/** Apply tools with current session cwd. */
|
|
330
332
|
apply(enabled: boolean, cwd: string): void {
|
|
331
333
|
this.enabled = enabled;
|
|
332
|
-
this.
|
|
333
|
-
registerRoundedTools(this.originalRegisterTool, enabled, cwd, this.captured);
|
|
334
|
-
this.reRegisterCaptured();
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
/**
|
|
338
|
-
* Install a one-time proxy on `pi.registerTool` to intercept third-party
|
|
339
|
-
* tool registrations.
|
|
340
|
-
*/
|
|
341
|
-
private installProxy(): void {
|
|
342
|
-
if (this.proxyInstalled) return;
|
|
343
|
-
this.proxyInstalled = true;
|
|
344
|
-
|
|
345
|
-
this.originalRegisterTool = this.pi.registerTool.bind(this.pi);
|
|
346
|
-
const original = this.originalRegisterTool;
|
|
347
|
-
const self = this;
|
|
348
|
-
|
|
349
|
-
this.pi.registerTool = function proxyRegisterTool(tool: ToolDef) {
|
|
350
|
-
// Don't intercept tools that already render their own shell
|
|
351
|
-
if (tool.renderShell === "self") {
|
|
352
|
-
original(tool);
|
|
353
|
-
return;
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
// Capture the unwrapped original definition
|
|
357
|
-
self.captured.set(tool.name, tool);
|
|
358
|
-
original(self.enabled ? wrapBuiltin(tool) : tool);
|
|
359
|
-
};
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
/**
|
|
363
|
-
* Re-register all captured third-party tools with or without rounded
|
|
364
|
-
* frames based on the current `enabled` state.
|
|
365
|
-
*/
|
|
366
|
-
private reRegisterCaptured(): void {
|
|
367
|
-
for (const def of this.captured.values()) {
|
|
368
|
-
this.originalRegisterTool(this.enabled ? wrapBuiltin(def) : def);
|
|
369
|
-
}
|
|
334
|
+
registerRoundedTools(this.pi, enabled, cwd);
|
|
370
335
|
}
|
|
371
336
|
}
|
|
372
|
-
|