fix: add final unmount render to trigger shouldClearOnUnmount
Ink does an explicit onRender() with isUnmounting=true before unmount (ink.tsx:755-761), which triggers clearTerminal for fullscreen apps. vue-tui's teardown() was nulling scheduledCommit before unmount, making shouldClearOnUnmount dead code. Now calls commit() synchronously before disabling the scheduler, matching Ink's unmount render behavior. Restores 2 erase PTY tests that were incorrectly attributed to a React vs Vue rendering difference. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -51,6 +51,26 @@ it("do not erase screen where <Static> is taller than viewport", async () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("erase screen (content overflows viewport)", async () => {
|
||||
const ps = term("erase", ["3"]);
|
||||
await ps.waitForExit();
|
||||
expect(ps.output).toContain(ansiEscapes.clearTerminal);
|
||||
|
||||
for (const letter of ["A", "B", "C"]) {
|
||||
expect(ps.output).toContain(letter);
|
||||
}
|
||||
});
|
||||
|
||||
it("erase screen where <Static> exists but interactive part is taller than viewport", async () => {
|
||||
const ps = term("erase-with-static", ["3"]);
|
||||
await ps.waitForExit();
|
||||
expect(ps.output).toContain(ansiEscapes.clearTerminal);
|
||||
|
||||
for (const letter of ["A", "B", "C", "D", "E", "F"]) {
|
||||
expect(ps.output).toContain(letter);
|
||||
}
|
||||
});
|
||||
|
||||
it("erase screen where state changes", async () => {
|
||||
const ps = term("erase-with-state-change", ["4"]);
|
||||
await ps.waitForExit();
|
||||
|
||||
@@ -150,6 +150,7 @@ export function createApp(root: Component, rootProps?: RootProps | null): TuiApp
|
||||
let mountedGetLastOutput: (() => string) | null = null;
|
||||
let mountedRestoreConsole: (() => void) | null = null;
|
||||
let mountedScheduler: ReturnType<typeof createCommitScheduler> | null = null;
|
||||
let mountedCommit: (() => void) | null = null;
|
||||
|
||||
// The renderer's onCommit closure is wired at createApp time but only does
|
||||
// real work after mount swaps in scheduler.schedule. One renderer per app
|
||||
@@ -185,6 +186,14 @@ export function createApp(root: Component, rootProps?: RootProps | null): TuiApp
|
||||
function teardown() {
|
||||
if (teardownStarted) return;
|
||||
teardownStarted = true;
|
||||
|
||||
// Final render before unmount (matching Ink ink.tsx:755-761).
|
||||
// teardownStarted=true makes shouldClearTerminalForFrame see isUnmounting,
|
||||
// so fullscreen apps get clearTerminal on exit.
|
||||
if (mountedInteractive && !mountedDebug && mountedCommit) {
|
||||
mountedCommit();
|
||||
}
|
||||
|
||||
scheduledCommit = () => {};
|
||||
// Restore console BEFORE Vue cleanup (matching Ink ink.tsx:779)
|
||||
if (mountedRestoreConsole) {
|
||||
@@ -523,6 +532,7 @@ export function createApp(root: Component, rootProps?: RootProps | null): TuiApp
|
||||
}
|
||||
const scheduler = createCommitScheduler(commit, schedulerOptions);
|
||||
mountedScheduler = scheduler;
|
||||
mountedCommit = commit;
|
||||
scheduledCommit = scheduler.schedule;
|
||||
|
||||
// Internal provides — set before the actual mount so components can inject
|
||||
|
||||
Reference in New Issue
Block a user