fix(editor): DOM-level blue-screen floor — React can no longer white-screen a crash

v0.1.22's WasmErrorBoundary was still not enough: a commit-phase throw in
WasmTool's OWN effects unmounts the root, and no boundary below it helps.
fatal-screen.ts is the floor: plain-DOM blue screen with its own mirrored
log ring (append feeds recordFatalLog), installed at module import in
main.tsx — before and independent of React. It cooperates with the React
overlay: hidden while [data-testid="fatal-overlay"] exists, takes over via a
1Hz ensure-loop the moment it disappears. Fatal promotions also append the
asyncify flight-recorder dump so whichever screen survives carries the
targeting data.

fatal-overlay.spec.ts now also rips out the React root after the fatal and
asserts the DOM floor takes over with the mirrored [fatal] log.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019SE4o46Lnq3hF574FFq8x4
This commit is contained in:
Gergő Törcsvári 2026-08-01 14:07:09 +02:00
commit eff5befd5d
No known key found for this signature in database
GPG key ID: 8E75F2CDE64E5322
5 changed files with 209 additions and 4 deletions

View file

@ -49,4 +49,17 @@ test('a terminal uncaught error raises the blue fatal overlay with the console o
await expect(page.locator('pre').filter({ hasText: '[fatal]' })).toBeVisible({
timeout: 10000,
});
// While React's overlay is alive, the DOM floor stays out of the way.
await expect(page.getByTestId('fatal-screen-dom')).toHaveCount(0);
// Simulate what prod actually did three releases in a row: React unmounts
// its whole root after the fatal. The DOM-level floor must take over —
// blue screen + the mirrored log — instead of a white page.
await page.evaluate(() => {
document.querySelector('[data-testid="fatal-overlay"]')?.closest('#root, body > div')?.remove();
});
await expect(page.getByTestId('fatal-screen-dom')).toBeVisible({ timeout: 5000 });
await expect(page.locator('#pcbjam-fatal-screen pre')).toContainText('[fatal]', {
timeout: 5000,
});
});