fix: address PR review — flush hang on cancel, BSU/ESU incremental wrapping
- scheduler.cancel() now resolves a pending flush() waiter, preventing waitUntilRenderFlush() from hanging when the app unmounts mid-flush - BSU/ESU now wrap the actual stream writes instead of being embedded in the frame string, so synchronization survives log-update's incremental line diffing (matches Ink ink.tsx:1059-1097). Normal branch guarded by willRender() to avoid empty synchronized-update pairs on unchanged frames Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -490,19 +490,29 @@ export function createApp(root: Component, rootProps?: RootProps | null): TuiApp
|
||||
});
|
||||
|
||||
if (shouldClear) {
|
||||
// Direct write: clearTerminal + accumulated static + raw output
|
||||
const content = ansiEscapes.clearTerminal + frameState.fullStaticOutput + output;
|
||||
stdout.write(synchronize ? bsu + content + esu : content);
|
||||
// Direct write: clearTerminal + accumulated static + raw output.
|
||||
// BSU/ESU wrap the actual stream writes (not embedded in the frame
|
||||
// string) so synchronization survives log-update's line diffing.
|
||||
if (synchronize) stdout.write(bsu);
|
||||
stdout.write(ansiEscapes.clearTerminal + frameState.fullStaticOutput + output);
|
||||
// Sync log-update state so next render computes correct erase
|
||||
writer.sync(outputToRender);
|
||||
if (synchronize) stdout.write(esu);
|
||||
} else if (hasStaticOutput) {
|
||||
// Clear frame -> write static -> re-render frame via log-update
|
||||
if (synchronize) stdout.write(bsu);
|
||||
writer.clear();
|
||||
stdout.write(staticOutput);
|
||||
writer.write(synchronize ? outputToRender + esu : outputToRender);
|
||||
writer.write(outputToRender);
|
||||
if (synchronize) stdout.write(esu);
|
||||
} else if (synchronize && writer.willRender(outputToRender)) {
|
||||
// Only emit BSU/ESU when log-update will actually write, so unchanged
|
||||
// frames don't produce empty synchronized-update pairs.
|
||||
stdout.write(bsu);
|
||||
writer.write(outputToRender);
|
||||
stdout.write(esu);
|
||||
} else {
|
||||
writer.write(synchronize ? bsu + outputToRender + esu : outputToRender);
|
||||
writer.write(outputToRender);
|
||||
}
|
||||
|
||||
frameState.lastOutput = output;
|
||||
|
||||
@@ -98,6 +98,11 @@ export function createCommitScheduler(
|
||||
}
|
||||
hasPendingFlag = false;
|
||||
scheduled = false;
|
||||
// Resolve any waiter blocked on flush() — the pending commit will never
|
||||
// fire now, so leaving resolveFlush unsettled would hang waitUntilRenderFlush.
|
||||
const r = resolveFlush;
|
||||
resolveFlush = null;
|
||||
r?.();
|
||||
}
|
||||
|
||||
return { schedule, flush, hasPending, cancel };
|
||||
|
||||
Reference in New Issue
Block a user