mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-28 19:01:34 +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
123 lines
5.7 KiB
JavaScript
123 lines
5.7 KiB
JavaScript
// #276: production-bundle Preview -> Apply -> reload -> one-shot Undo for an
|
|
// exact partition which duplicates one solid shared room wall and hosts a door.
|
|
import { launch, checkAll, finish } from './serve.mjs';
|
|
|
|
const { page, browser } = await launch({ width: 900, height: 820 });
|
|
const out = await page.evaluate(async () => {
|
|
const result = {};
|
|
const card = window.__card;
|
|
const clone = (value) => JSON.parse(JSON.stringify(value));
|
|
const wallHostResolves = (space, opening) => opening.host?.kind === 'wall'
|
|
&& (space.wall_segments || []).some((segment) => segment.id === opening.host.id);
|
|
const sharedX = 0.5;
|
|
const original = {
|
|
model_version: 7,
|
|
spaces: [{
|
|
id: 'coincident', title: 'Coincident', view_box: [0, 0, 1, 1], cell_cm: 5,
|
|
rooms: [
|
|
{ id: 'left', name: 'Left', area: null,
|
|
poly: [[0, 0], [sharedX, 0], [sharedX, 1], [0, 1]] },
|
|
{ id: 'right', name: 'Right', area: null,
|
|
poly: [[sharedX, 0], [1, 0], [1, 1], [sharedX, 1]] },
|
|
],
|
|
walls: [{
|
|
key: '500.000000,500.000000@1.5706',
|
|
a: [sharedX, 0], b: [sharedX, 1], cm: 20,
|
|
}],
|
|
partitions: [{ id: 'redundant', a: [sharedX, 0], b: [sharedX, 1], cm: 20 }],
|
|
openings: [{
|
|
id: 'door', type: 'door', x: 0, y: 0, angle: 0, length: 0.2,
|
|
contact: 'binary_sensor.test_door', lock: 'lock.test_door', flip_h: true,
|
|
host: { kind: 'partition', id: 'redundant', t: 0.5 },
|
|
}],
|
|
}],
|
|
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);
|
|
backup = null;
|
|
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._modelCache = null; card._frame = null; card._space = 'coincident';
|
|
card.requestUpdate(); await card.updateComplete;
|
|
|
|
card._editorRuntime.optimizePlans.open(); await card.updateComplete;
|
|
const preview = card._alignDialog;
|
|
const previewSpace = preview?.config?.spaces?.[0];
|
|
result.previewIsExact = !!preview?.changed && preview.preflight?.ok === true
|
|
&& preview.report.partitionsReconciled === 1
|
|
&& preview.report.openingsRehosted === 1
|
|
&& preview.config.model_version === 10
|
|
&& previewSpace.partitions == null
|
|
&& wallHostResolves(previewSpace, previewSpace.openings[0]);
|
|
result.previewDoesNotWrite = sent.length === 0
|
|
&& JSON.stringify(card._serverCfg) === JSON.stringify(original);
|
|
result.reportRendersBothCounters = card.renderRoot.querySelectorAll('hp-dialog .alignmsg').length >= 2;
|
|
|
|
await card._editorRuntime.optimizePlans.run(); await card.updateComplete;
|
|
const applied = card._serverCfg.spaces[0];
|
|
result.applyUsesOneAtomicWrite = sent.filter((type) => type === 'houseplan/plan/optimize').length === 1;
|
|
result.applyKeepsOpeningFields = applied.partitions == null
|
|
&& wallHostResolves(applied, applied.openings[0])
|
|
&& applied.openings[0].id === 'door'
|
|
&& applied.openings[0].contact === 'binary_sensor.test_door'
|
|
&& applied.openings[0].lock === 'lock.test_door'
|
|
&& applied.openings[0].flip_h === true
|
|
&& Math.abs(applied.openings[0].x - sharedX) < 1e-9
|
|
&& Math.abs(applied.openings[0].y - 0.5) < 1e-9;
|
|
result.applyEnablesUndo = card._canOptimizeUndo === true;
|
|
|
|
await card._loadFromServer(); await card.updateComplete;
|
|
result.reloadKeepsCanonicalBody = card._serverCfg.spaces[0].partitions == null
|
|
&& wallHostResolves(card._serverCfg.spaces[0], card._serverCfg.spaces[0].openings[0]);
|
|
|
|
await card._undoPlanOptimization(); await card.updateComplete;
|
|
result.undoRestoresHostedPartition = JSON.stringify(card._serverCfg.spaces[0].partitions)
|
|
=== JSON.stringify(original.spaces[0].partitions)
|
|
&& JSON.stringify(card._serverCfg.spaces[0].openings)
|
|
=== JSON.stringify(original.spaces[0].openings)
|
|
&& card._canOptimizeUndo === false;
|
|
|
|
// Apply once more and prove the ordinary shared wall is no longer hidden
|
|
// underneath an independent-wall blocker in the remaining Thickness tool.
|
|
card._editorRuntime.optimizePlans.open(); await card.updateComplete;
|
|
await card._editorRuntime.optimizePlans.run(); await card.updateComplete;
|
|
card._setMode('plan'); card._markup = true;
|
|
card._modelCache = null; card.requestUpdate(); await card.updateComplete;
|
|
card._tool = 'wallthick';
|
|
card._wallThickClick([500, 500]);
|
|
result.thicknessToolSelectsCanonicalWall = !!card._wallDialog;
|
|
card._wallDialog = { ...card._wallDialog, value: '10' };
|
|
card._wallThickApply(false); await card.updateComplete;
|
|
const finalSpace = card._serverCfg.spaces[0];
|
|
result.thicknessChangesSingleBody = finalSpace.partitions == null
|
|
&& finalSpace.walls.length === 1 && finalSpace.walls[0].cm === 10
|
|
&& wallHostResolves(finalSpace, finalSpace.openings[0]);
|
|
return result;
|
|
});
|
|
|
|
await finish(browser, checkAll(out));
|