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 () => {
|
it("erase screen where state changes", async () => {
|
||||||
const ps = term("erase-with-state-change", ["4"]);
|
const ps = term("erase-with-state-change", ["4"]);
|
||||||
await ps.waitForExit();
|
await ps.waitForExit();
|
||||||
|
|||||||
@@ -150,6 +150,7 @@ export function createApp(root: Component, rootProps?: RootProps | null): TuiApp
|
|||||||
let mountedGetLastOutput: (() => string) | null = null;
|
let mountedGetLastOutput: (() => string) | null = null;
|
||||||
let mountedRestoreConsole: (() => void) | null = null;
|
let mountedRestoreConsole: (() => void) | null = null;
|
||||||
let mountedScheduler: ReturnType<typeof createCommitScheduler> | 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
|
// 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
|
// 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() {
|
function teardown() {
|
||||||
if (teardownStarted) return;
|
if (teardownStarted) return;
|
||||||
teardownStarted = true;
|
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 = () => {};
|
scheduledCommit = () => {};
|
||||||
// Restore console BEFORE Vue cleanup (matching Ink ink.tsx:779)
|
// Restore console BEFORE Vue cleanup (matching Ink ink.tsx:779)
|
||||||
if (mountedRestoreConsole) {
|
if (mountedRestoreConsole) {
|
||||||
@@ -523,6 +532,7 @@ export function createApp(root: Component, rootProps?: RootProps | null): TuiApp
|
|||||||
}
|
}
|
||||||
const scheduler = createCommitScheduler(commit, schedulerOptions);
|
const scheduler = createCommitScheduler(commit, schedulerOptions);
|
||||||
mountedScheduler = scheduler;
|
mountedScheduler = scheduler;
|
||||||
|
mountedCommit = commit;
|
||||||
scheduledCommit = scheduler.schedule;
|
scheduledCommit = scheduler.schedule;
|
||||||
|
|
||||||
// Internal provides — set before the actual mount so components can inject
|
// Internal provides — set before the actual mount so components can inject
|
||||||
|
|||||||
Reference in New Issue
Block a user