fix: Shift listeners die with every palette exit, not only placement (#369 r2-H1)

The r2 reviewer proved two real exits leaked the window keydown/keyup
listeners of the furniture preview — Escape (the card closes the palette
by direct assignment, bypassing the runtime paths) and disconnectedCallback
(only _clearFurniturePreview ran). The arrow-field listener pins the whole
editor runtime and, through it, the card: the exact leak class this
disconnectedCallback already names two cases above. Both sites now call
_furnShiftDetach alongside the preview cleanup, the smoke gained the
Escape regression (re-arm attaches again, Escape closes the palette AND
brings removes level with adds), and the idempotent double detach in
_furnPlace (r2 Low) is gone.

Issue: #369
User-Visible: no
This commit is contained in:
Codex
2026-08-29 11:30:11 +03:00
parent 8ee1c91fff
commit 2c984d8f9d
19 changed files with 155 additions and 135 deletions
+17
View File
@@ -90,6 +90,23 @@ const out = await page.evaluate(async () => {
out.shiftAfterDetachIsInert = !c._furnPreviewInput;
window.dispatchEvent(new KeyboardEvent('keyup', { key: 'Shift', bubbles: true }));
// r2-H1: Escape с armed-символом тоже снимает слушатели (путь мимо place)
c._editorSecondary.openPalette?.();
c._decorTool = 'furniture';
c._furnPalette = null; c._furnCategory = null;
c.requestUpdate(); await c.updateComplete;
pal()?.querySelector('.furnitem[data-category="sofa"]')?.click();
await c.updateComplete;
pal()?.querySelector('.furnitem[data-symbol="sofa"]')?.click();
await c.updateComplete;
const addsBeforeEscape = shiftAdds;
out.rearmAttachesAgain = shiftAdds === shiftRemoves + 2;
window.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }));
c._onKey?.(new KeyboardEvent('keydown', { key: 'Escape' }));
await c.updateComplete;
out.escapeClosesPalette = !c._furnPalette && c._decorTool === 'select';
out.escapeDetachesListeners = shiftRemoves === addsBeforeEscape;
window.addEventListener = addReal;
window.removeEventListener = removeReal;
// прибрать поставленный диван