mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-29 03:09:36 +00:00
Первый вынос из монолита по образцу live-*/RadarSetupController. src/optimize-plans-dialog.ts: OptimizePlansDialogState, чистые preflightDiagnostics/preflightVersionsDiffer и класс OptimizePlansDialog (open/preview/toggleLivePositions/run/copyDiagnostics/ reportPreflightFailure/render, clipboardFallback) за портом из 18 членов. Модуль не знает HouseplanEditorHostPort; импортирует его только рантайм, карточка берёт тип. Инлайн-фолбэк буфера обмена — WeakMap по объекту диалога (CODE-REVIEW-295-r1 M2), дедуп dev-лога — поле класса: из карточки и порта ушли _preflightClipboardFallback и _reportedPreflightFingerprint, пять делегатов и две стрелки-заглушки. Харнесс (13 смоков, wall-draw-click, golden) зовёт card._editorRuntime.optimizePlans.* — меняется только адрес. 11 текстовых утверждений i18n.test.mjs о разметке и тосте диалога и гварды 8 мутантов переехали в юнит test/optimize-plans-dialog.test.mjs (test-build); новый мутант optimize-dialog-imports-host-port. monolith-baseline: delegates 159→154, portMembers 350→348, hostRefs 4948→4869, portPrivates 96→94, harnessPrivates 107→101, bundleBytes 2499647→2499182. Потолки ядер опущены на выигрыш; потолок lazy editor gzip пересчитан (+555 Б gzip при −465 Б сырых). Issue: #642 User-Visible: no
103 lines
4.3 KiB
JavaScript
103 lines
4.3 KiB
JavaScript
// #290: production Walls/Optimize path for the tracked 316x1 shared wall.
|
|
import { readFileSync } from 'node:fs';
|
|
import { launch, checkAll, finish } from './serve.mjs';
|
|
|
|
const space = JSON.parse(readFileSync(
|
|
new URL('../test/fixtures/279-near-orthogonal-junction.json', import.meta.url), 'utf8',
|
|
));
|
|
const { page, browser } = await launch({ width: 980, height: 900 });
|
|
|
|
const out = await page.evaluate(async (sourceSpace) => {
|
|
const result = {};
|
|
const card = window.__card;
|
|
const clone = (value) => JSON.parse(JSON.stringify(value));
|
|
const original = {
|
|
model_version: 7,
|
|
spaces: [{ id: 'near-axis', title: 'Near axis', view_box: [-2, 2, 3.2, 2], ...sourceSpace }],
|
|
markers: [], settings: {},
|
|
};
|
|
let serverConfig = clone(original), serverLayout = {}, backup = null;
|
|
const sent = [];
|
|
const baseCall = card.hass.callWS.bind(card.hass);
|
|
card.hass = {
|
|
...card.hass,
|
|
callWS: async (message) => {
|
|
if (message.type === 'houseplan/plan/optimize') {
|
|
sent.push(message.type);
|
|
backup = { config: clone(serverConfig), layout: clone(serverLayout) };
|
|
serverConfig = clone(message.config);
|
|
serverLayout = clone(message.layout);
|
|
return { ok: true, config_rev: 2, layout_rev: 2, can_undo: true };
|
|
}
|
|
if (message.type === 'houseplan/plan/optimize_undo') {
|
|
sent.push(message.type);
|
|
serverConfig = clone(backup.config);
|
|
serverLayout = clone(backup.layout);
|
|
return { ok: true, config_rev: 3, layout_rev: 3, can_undo: false };
|
|
}
|
|
if (message.type === 'houseplan/config/get') {
|
|
return { config: clone(serverConfig), rev: 3, can_write: true, can_optimize_undo: !!backup };
|
|
}
|
|
if (message.type === 'houseplan/layout/get') return { layout: clone(serverLayout), rev: 3 };
|
|
return baseCall(message);
|
|
},
|
|
};
|
|
card._serverCfg = clone(original);
|
|
card._layout = {};
|
|
card._space = 'near-axis';
|
|
card._modelCache = null;
|
|
card._frame = null;
|
|
card.requestUpdate();
|
|
await card.updateComplete;
|
|
|
|
card._editorRuntime.optimizePlans.open();
|
|
await card.updateComplete;
|
|
const preview = card._alignDialog;
|
|
const dialogText = card.renderRoot.querySelector('hp-dialog')?.textContent || '';
|
|
result.previewCountsPhysicalWallOnce = preview?.report.wallsStraightened === 1
|
|
&& preview.report.wallsStraightenSkipped === 0
|
|
&& Math.abs(preview.report.maxStraightenShiftCm - 1) < 1e-6;
|
|
result.previewUsesProductionPreflight = preview?.preflight?.ok === true;
|
|
result.previewNamesLossyRepair = dialogText.includes(card._t('gs.optimize_walls_straightened', {
|
|
n: '1', cm: '1',
|
|
}));
|
|
result.previewDoesNotWrite = sent.length === 0
|
|
&& JSON.stringify(card._serverCfg) === JSON.stringify(original);
|
|
|
|
card._alignDialog = null;
|
|
await card.updateComplete;
|
|
result.cancelDoesNotWrite = sent.length === 0;
|
|
|
|
card._editorRuntime.optimizePlans.open();
|
|
await card.updateComplete;
|
|
await card._editorRuntime.optimizePlans.run();
|
|
await card.updateComplete;
|
|
const north = card._serverCfg.spaces[0].rooms.find((room) => room.id === 'north-west');
|
|
const south = card._serverCfg.spaces[0].rooms.find((room) => room.id === 'south-west');
|
|
result.applyUsesOneAtomicWrite = sent.filter((type) => type === 'houseplan/plan/optimize').length === 1;
|
|
const samePoint = (left, right) => JSON.stringify(left) === JSON.stringify(right);
|
|
result.applyMovesBothOwnersToExactAxis = north.poly[1][1] === north.poly[2][1]
|
|
&& south.poly.some((point) => samePoint(point, north.poly[1]))
|
|
&& south.poly.some((point) => samePoint(point, north.poly[2]));
|
|
result.applyRekeysWall = card._serverCfg.spaces[0].walls.some((wall) => (
|
|
wall.a[1] === wall.b[1] && wall.key.endsWith('@0.0000')
|
|
));
|
|
|
|
await card._loadFromServer();
|
|
await card.updateComplete;
|
|
card._editorRuntime.optimizePlans.open();
|
|
await card.updateComplete;
|
|
result.reloadIsIdempotent = card._alignDialog.report.wallsStraightened === 0;
|
|
card._alignDialog = null;
|
|
|
|
await card._undoPlanOptimization();
|
|
await card.updateComplete;
|
|
result.undoRestoresExactGeometry = JSON.stringify(card._serverCfg.spaces)
|
|
=== JSON.stringify(original.spaces);
|
|
result.undoServerSnapshotIsByteExact = JSON.stringify(serverConfig) === JSON.stringify(original);
|
|
result.undoIsOneDeep = card._canOptimizeUndo === false;
|
|
return result;
|
|
}, space);
|
|
|
|
await finish(browser, checkAll(out));
|