fix(coding-agent): preserve TUI wrapper routing (closes #7731)

This commit is contained in:
Armin Ronacher 2026-08-07 00:40:44 +02:00
parent 7cb2a51c05
commit 666d8972ff
3 changed files with 44 additions and 4 deletions

View file

@ -13,6 +13,7 @@
### Fixed
- Fixed extension TUI method wrappers recursing indefinitely when delegating to the original method ([#7731](https://github.com/earendil-works/pi/issues/7731)).
- Fixed right-click not pasting clipboard text in fullscreen mode on Windows.
## [0.84.0] - 2026-08-06

View file

@ -358,11 +358,19 @@ export function createInteractiveTuiReference(getTui: () => TUI): TUI {
const tui = getTui();
const value = Reflect.get(tui, property, tui);
if (typeof value !== "function") return value;
let methodTui = tui;
let method = value;
return (...args: unknown[]) => {
const tui = getTui();
const method = Reflect.get(tui, property, tui);
if (typeof method !== "function") throw new TypeError(`TUI property ${String(property)} is not callable`);
return Reflect.apply(method, tui, args);
const currentTui = getTui();
if (currentTui !== methodTui) {
const currentMethod = Reflect.get(currentTui, property, currentTui);
if (typeof currentMethod !== "function") {
throw new TypeError(`TUI property ${String(property)} is not callable`);
}
methodTui = currentTui;
method = currentMethod;
}
return Reflect.apply(method, methodTui, args);
};
},
set: (_target, property, value) => {

View file

@ -0,0 +1,31 @@
import type { TUI } from "@earendil-works/pi-tui";
import { describe, expect, it, vi } from "vitest";
import { createInteractiveTuiReference } from "../../../src/modes/interactive/interactive-mode.ts";
describe("TUI method wrapping", () => {
it("calls the method captured before a replacement", () => {
const renderer = {
render: (width: number) => [`width: ${width}`],
} as unknown as TUI;
const tui = createInteractiveTuiReference(() => renderer);
const originalRender = tui.render;
tui.render = (width: number) => originalRender(width);
expect(tui.render(80)).toEqual(["width: 80"]);
});
it("routes a captured method to a replacement renderer", () => {
const regularRequestRender = vi.fn();
const fullscreenRequestRender = vi.fn();
let renderer = { requestRender: regularRequestRender } as unknown as TUI;
const tui = createInteractiveTuiReference(() => renderer);
const requestRender = tui.requestRender;
requestRender();
renderer = { requestRender: fullscreenRequestRender } as unknown as TUI;
requestRender();
expect(regularRequestRender).toHaveBeenCalledOnce();
expect(fullscreenRequestRender).toHaveBeenCalledOnce();
});
});