Files
houseplan-card/demo/smoke_furniture_polish.mjs
T
Codex 8ee1c91fff fix: seven small honesty fixes from the 29.08 audit (#369)
(a) documented: deleting a vacuum marker erases its server trail at once
and a re-added marker starts from scratch (VACUUM.md + both USER-GUIDEs).
(b) smoothVacPath reports dropped non-finite segments — one console warn
per call with the count — instead of hiding the whole trail silently on a
broken calibration matrix. (c) room climate (#317) now reaches legacy
markers whose exported config carries an ABSENT area key rather than an
explicit null: `== null` where the placement is decided. (d) the armed
furniture preview follows Shift without mouse movement — window
keydown/keyup listeners live exactly as long as the palette is armed,
detached at every palette teardown. (e) only the primary mouse button
places decor/furniture: a right or middle click with an armed tool is a
no-op, touch/pen untouched. (f) a device whose registry entities were ALL
deliberately disabled by the user no longer glows as an alive controller —
the #318 entityless-active rule now requires a genuinely empty roster.
(g) furniture-pack author corrected to Sergey Matyunin (Сергей Матюнин)
per the owner's decision — LICENSE.md, README.md, pack.json,
docs/FURNITURE.md, the provenance check in generate-furniture-assets and
its unit; the source archive bytes are unchanged and the README notes the
romanisation fix.

Proofs: units for (b)/(c)/(f) including the #318 regression pair; new
smoke_furniture_polish for (d)/(e) with listener add/remove counters and
both mouse buttons; five registry mutants, one per code change.

Issue: #369
User-Visible: yes
2026-08-29 11:29:42 +03:00

104 lines
4.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// #369(д)(е): the armed furniture preview follows Shift without mouse motion,
// its window listeners come and go with the palette, and only the primary
// mouse button places objects — a right-click with an armed tool is a no-op.
import { launch, checkAll, finish } from './serve.mjs';
const { page, browser } = await launch();
const out = await page.evaluate(async () => {
const out = {};
const c = window.__card;
const sr = () => c.shadowRoot || c.renderRoot;
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const stageEl = () => sr().querySelector('.stage');
const toScreen = (x, y) => {
const r = stageEl().getBoundingClientRect();
const v = c._viewOr(c._baseVb());
return [r.left + ((x - v.x) / v.w) * r.width, r.top + ((y - v.y) / v.h) * r.width * (v.h / v.w) * (r.height / (r.width * (v.h / v.w)))];
};
// счётчики слушателей окна
const addReal = window.addEventListener.bind(window);
const removeReal = window.removeEventListener.bind(window);
let shiftAdds = 0; let shiftRemoves = 0;
window.addEventListener = (type, fn, opts) => {
if (type === 'keydown' || type === 'keyup') shiftAdds++;
return addReal(type, fn, opts);
};
window.removeEventListener = (type, fn, opts) => {
if (type === 'keydown' || type === 'keyup') shiftRemoves++;
return removeReal(type, fn, opts);
};
// в декор-режим, инструмент «мебель», выбрать символ
await c._requestMode('decor', false);
await c.updateComplete;
c._editorSecondary.openPalette?.();
c._decorTool = 'furniture';
c._furnPalette = null; c._furnCategory = null;
c.requestUpdate(); await c.updateComplete;
const pal = () => sr().querySelector('.furnpalette');
pal()?.querySelector('.furnitem[data-category="sofa"]')?.click();
await c.updateComplete;
pal()?.querySelector('.furnitem[data-symbol="sofa"]')?.click();
await c.updateComplete;
out.symbolArmed = !!c._furnPalette;
out.listenersAttached = shiftAdds >= 2;
// превью от движения мыши
const stage = stageEl();
const rect = stage.getBoundingClientRect();
const cx = rect.left + rect.width * 0.5;
const cy = rect.top + rect.height * 0.55;
stage.dispatchEvent(new PointerEvent('pointermove', {
bubbles: true, composed: true, pointerType: 'mouse', pointerId: 7,
clientX: cx, clientY: cy,
}));
await c.updateComplete;
out.previewVisible = !!c._furnPreviewInput;
out.previewStartsSnapped = c._furnPreviewInput?.free === false;
// (д): Shift БЕЗ движения мыши переводит превью в free и обратно
window.dispatchEvent(new KeyboardEvent('keydown', { key: 'Shift', bubbles: true }));
await c.updateComplete;
out.shiftDownGoesFree = c._furnPreviewInput?.free === true;
window.dispatchEvent(new KeyboardEvent('keyup', { key: 'Shift', bubbles: true }));
await c.updateComplete;
out.shiftUpBacksToSnap = c._furnPreviewInput?.free === false;
// (е): правый клик с armed-символом НЕ ставит мебель
const beforeCount = c._decorList.length;
stage.dispatchEvent(new PointerEvent('pointerdown', {
bubbles: true, composed: true, pointerType: 'mouse', pointerId: 8,
clientX: cx, clientY: cy, button: 2, buttons: 2,
}));
await c.updateComplete; await sleep(50);
out.rightClickPlacesNothing = c._decorList.length === beforeCount
&& !!c._furnPalette;
// левый клик — ставит (регресс)
stage.dispatchEvent(new PointerEvent('pointerdown', {
bubbles: true, composed: true, pointerType: 'mouse', pointerId: 9,
clientX: cx, clientY: cy, button: 0, buttons: 1,
}));
await c.updateComplete; await sleep(50);
out.leftClickPlaces = c._decorList.length === beforeCount + 1;
// (д): после установки палитра погашена — слушатели сняты симметрично
out.paletteDisarmed = !c._furnPalette;
out.listenersDetached = shiftRemoves >= 2 && shiftRemoves === shiftAdds;
// Shift после снятия — не трогает ничего (превью нет)
window.dispatchEvent(new KeyboardEvent('keydown', { key: 'Shift', bubbles: true }));
out.shiftAfterDetachIsInert = !c._furnPreviewInput;
window.dispatchEvent(new KeyboardEvent('keyup', { key: 'Shift', bubbles: true }));
window.addEventListener = addReal;
window.removeEventListener = removeReal;
// прибрать поставленный диван
const sp = c._curSpaceCfg;
sp.decor = sp.decor.slice(0, beforeCount);
c._decorSel = null;
c.requestUpdate(); await c.updateComplete;
return out;
});
checkAll(out);
await finish(browser, out);