refactor: remove dead partition tool state

Issue: #176
User-Visible: no
This commit is contained in:
Sergey Matyunin
2026-08-19 16:17:09 +03:00
parent 41e2cfffc6
commit f66e671b89
16 changed files with 142 additions and 127 deletions
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -435,7 +435,7 @@ export async function prepareGoldenScenario(page, scenario) {
const { tool, anchor, pointer, expectedKind } = scenario.planSnap;
const validPoint = (point) => Array.isArray(point) && point.length === 2
&& point.every(Number.isFinite);
if (!['draw', 'partition'].includes(tool) || !validPoint(pointer)
if (tool !== 'draw' || !validPoint(pointer)
|| (anchor != null && !validPoint(anchor))
|| !['endpoint', 'line'].includes(expectedKind)) {
throw new Error(`invalid golden planSnap contract: ${scenario.id}`);
+21 -8
View File
@@ -52,12 +52,28 @@ const out = await page.evaluate(async () => {
// Keep the smoke local: editor transactions still mutate the real config,
// invalidate derived models and history, but do not need a websocket write.
c._saveConfig = () => { c._cfgEpoch++; c._modelCache = null; c.requestUpdate(); };
c.requestUpdate();
await c.updateComplete;
c._tool = 'partition';
c._drawWallField = '15';
c._partitionClick([100, 100], false);
c._partitionClick([300, 100], false);
const stage = (c.shadowRoot || c.renderRoot).querySelector('.stage');
const clickAt = (x, y) => {
const rect = stage.getBoundingClientRect();
const view = c._viewOr(c._baseVb());
return new MouseEvent('click', {
clientX: rect.left + ((x - view.x) / view.w) * rect.width,
clientY: rect.top + ((y - view.y) / view.h) * rect.height,
bubbles: true,
});
};
const drawAndFinish = (a, b, cm) => {
c._activateMarkupTool('draw');
c._drawWallField = String(cm);
c._markupClick(clickAt(...a));
c._markupClick(clickAt(...b));
c._activateMarkupTool('select');
};
drawAndFinish([100, 100], [300, 100], 15);
let sp = c._serverCfg.spaces[0];
o.partitionCreated = sp.partitions?.length === 1 && sp.partitions[0].cm === 15;
@@ -174,10 +190,7 @@ const out = await page.evaluate(async () => {
c._deletePhysicalSelection();
o.deleteOnDraftRemovesWholeOutline = !c._serverCfg.spaces[0].room_drafts;
c._tool = 'partition';
c._drawWallField = '120';
c._partitionClick([400, 100], false);
c._partitionClick([500, 100], false);
drawAndFinish([400, 100], [500, 100], 120);
o.invalidThicknessCreatesNothing = !c._serverCfg.spaces[0].partitions;
return o;
+10 -4
View File
@@ -182,9 +182,11 @@ const out = await page.evaluate(async () => {
await card.updateComplete;
result.currentAnchorDoesNotCreateZeroSegment = !active();
card._cancelPath();
card._tool = 'partition';
card._activateMarkupTool('draw');
await update();
card._markupClick(eventAt(735, 606, 'click'));
// Use a fresh point on the same physical line: the earlier assertion left
// 735,600 as a saved-draft endpoint, and public Walls correctly resumes it.
card._markupClick(eventAt(775, 606, 'click'));
await card.updateComplete;
result.tapWithoutHoverSnapsFirstPoint = card._path.length === 1
&& close(card._path[0][1], 600)
@@ -192,7 +194,9 @@ const out = await page.evaluate(async () => {
Math.round((card._path[0][0] - 600) / card._gridPitch));
card._markupClick(eventAt(896, 496, 'click'));
await card.updateComplete;
result.secondPartitionClickSnapsEndpoint = card._path.length === 0
card._activateMarkupTool('select');
await update();
result.secondWallsClickFinishesSnappedPartition = card._path.length === 0
&& card._curSpaceCfg.partitions.length === 2
&& close(card._curSpaceCfg.partitions[1].b[0] * 1000, 900)
&& close(card._curSpaceCfg.partitions[1].b[1] * 1000, 500);
@@ -202,6 +206,8 @@ const out = await page.evaluate(async () => {
drafts: card._curSpaceCfg.room_drafts,
partitions: card._curSpaceCfg.partitions,
});
card._activateMarkupTool('draw');
await update();
card._suppressClick = true;
card._markupClick(eventAt(700, 600, 'click'));
result.suppressedClickDoesNotCommit = card._path.length === 0;
@@ -222,7 +228,7 @@ const out = await page.evaluate(async () => {
partitions: card._curSpaceCfg.partitions,
}) === gestureGeometry;
card._tool = 'select';
card._activateMarkupTool('select');
await update();
result.otherPlanToolsHaveNoOverlay = !overlay();
card._setMode('view');
File diff suppressed because one or more lines are too long
+12 -12
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -22,7 +22,7 @@ metadata). Only an explicit owner-approved emergency hotfix may skip this gate.
| Item | State |
|---|---|
| Version | **v1.65.0-beta.4** everywhere (manifest, const.py, package.json, CARD_VERSION) — prerelease candidate for the current `S8-merged` queue |
| Current local cycle | v1.65.0-beta.4 candidate carries #180, #192, #193, #196 and #197: every remaining colour/opacity setting uses the unified picker with a real hue spectrum, open passages preview their exact future wall cut, room hover exposes humidity, and degenerate optional junction patches can no longer erase the canonical masonry, floor or light barriers. Publication waits for a green exact-SHA Validate and fail-closed prerelease preflight; v1.65.0-beta.3 is published and stable v1.64.0 remains unchanged. |
| Current local cycle | v1.65.0-beta.4 candidate carries #180, #192, #193, #196 and #197: every remaining colour/opacity setting uses the unified picker with a real hue spectrum, open passages preview their exact future wall cut, room hover exposes humidity, and degenerate optional junction patches can no longer erase the canonical masonry, floor or light barriers. #176 additionally removes the unreachable pre-#173 Partition tool-state while retaining legacy warm-token normalization and the persisted independent-wall model. Publication waits for a green exact-SHA Validate and fail-closed prerelease preflight; v1.65.0-beta.3 is published and stable v1.64.0 remains unchanged. |
| Hidden Labs Stage | #89 Stage 1 ships in v1.63.0-beta.1. #122 Stage 2 ships in v1.64.0 and evolves the same hidden, expiring `iso` experiment with matte walls, a low exterior floor edge, restrained shared shadows and live vertical door/window/gate panels. Flat remains default; editors and `houseplan-space-card` remain flat; live floor effects and HA actions remain unchanged. Public activation remains a separate task. |
| Workflow | Superseded 2026-08-12: the pre-1.62 rule of "local edits without tests or commits" is **dead** — since release 1.62 every product change follows `PROCESS.md` (issue in `S5-ready`+, branch `issue/<NN>-slug`, trailers on every commit, review pipeline; `AGENTS.md` is the summary). Release mechanics below remain current. A requested pre-release gets a production build plus the smallest targeted unit/smoke set covering the changed surfaces, one tested `dev` commit/tag and a GitHub Release with `prerelease=true`; `main` stays untouched. The complete local frontend/backend/smoke gate runs only before a stable release, after which `main` is fast-forwarded to the exact tested `dev` SHA and the GitHub Release uses `prerelease=false`. Release bodies are short and bilingual (Russian first): only significant user changes get individual bullets, while minor/code-only work is grouped as `Мелкие исправления и улучшения` / `Small fixes and improvements`; every body ends with separate links to the Russian and English changelogs. Detailed RU/EN changelog bullets may link the corresponding closed GitHub Issues; open or partially delivered issues are never presented as shipped. Telegram announcements are sent only for stable releases; beta and RC publication is silent. `docs/RELEASE-NOTES.md` is the current canonical body instance; `npm run release:prerelease -- <tag> --issues=… --yes` is the primary local publication path and the manual `Publish prerelease` workflow is its GitHub-only equivalent once present on `main`. Nothing is copied to the home instance by hand |
| GitHub | https://github.com/Matysh/houseplan-card — [Issues](https://github.com/Matysh/houseplan-card/issues) are the canonical task records; their labels carry priority and workflow status (`PROCESS.md` §9). GitHub Projects is no longer used. `main` carries stable releases; pre-release tags may point directly at `dev`. Work lands on `dev` and is merged into `main` for a stable release, so `dev` is normally equal to or ahead of `main`, never behind. Push via SSH key `ha_jb` (remote git@github.com:…); API releases via the fine-grained PAT in `~/.git-credentials` (Contents R/W, issued 2026-07-23) |
+7 -2
View File
@@ -487,14 +487,19 @@ separately promised workflows:
- [ ] The toolbar has one **Walls** tool and no separate Room outline or
Partition drawing button; Split remains available [auto:
smoke_unified_wall_tool]
smoke_unified_wall_tool + unified-wall-tool-source.test]
- [ ] Grid appears; dots snap; the wall chain draws pair-by-pair; shared walls reused
- [ ] Ruler: while drawing, the length of the current segment follows the cursor
(metres, or feet+inches on an imperial HA); scale = space "cm per cell" (default 5)
- [ ] Every completed segment is crash-safe in `room_drafts`. Changing tool,
editor or floor finishes an open chain as ordinary partitions in one
history/config transaction; the finished chain is not resumed as a draft
[auto: smoke_unified_wall_tool]
[auto: smoke_unified_wall_tool + smoke_free_walls +
smoke_plan_snap_overlay]
- [ ] A legacy warm-viewport token `partition` still opens the unified Walls
tool, but `partition` is not a runtime tool-state, dispatch branch or
golden-matrix option [auto: wall-face-graph.test +
unified-wall-tool-source.test + golden-matrix.test]
- [ ] Re-selecting Walls, Reset, pan, pinch, a second pointer, `pointercancel`
and a suppressed synthetic click never finish a chain or save an extra
segment [auto: smoke_unified_wall_tool]
Binary file not shown.

Before

Width:  |  Height:  |  Size: 282 KiB

After

Width:  |  Height:  |  Size: 282 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 286 KiB

After

Width:  |  Height:  |  Size: 286 KiB

+13 -13
View File
@@ -1,7 +1,7 @@
{
"version": 1,
"fixture": "synthetic-only",
"sourceFingerprint": "ccbc4fa08439a1d7a585a5b742035d048a8039fe4d023a48788f811884b6316c",
"sourceFingerprint": "edb4278c0d33b75e417cc0b2d83ac8a46f2be4cf0d2c46aed801c566d82b70bc",
"captureScriptSha256": "34f2219790d46efd8250e7a1bd829cb8fc0b0547e1260635fefa52407551b41b",
"command": "npm run build && node demo/docs/capture.mjs",
"scenarios": {
@@ -13,7 +13,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "ccbc4fa08439a1d7a585a5b742035d048a8039fe4d023a48788f811884b6316c",
"sourceSha256": "edb4278c0d33b75e417cc0b2d83ac8a46f2be4cf0d2c46aed801c566d82b70bc",
"imageSha256": "d36b6f9f8139f31ef73a780c6511a640a26055efd9d7a24c24fd48b1d8379bf0"
},
"view-touch": {
@@ -24,7 +24,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "ccbc4fa08439a1d7a585a5b742035d048a8039fe4d023a48788f811884b6316c",
"sourceSha256": "edb4278c0d33b75e417cc0b2d83ac8a46f2be4cf0d2c46aed801c566d82b70bc",
"imageSha256": "358e25ff9984d0fb0c03cfbb848df40c425fdfe64cd5f4f613b1e754ca9d4249"
},
"space-create": {
@@ -35,7 +35,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "ccbc4fa08439a1d7a585a5b742035d048a8039fe4d023a48788f811884b6316c",
"sourceSha256": "edb4278c0d33b75e417cc0b2d83ac8a46f2be4cf0d2c46aed801c566d82b70bc",
"imageSha256": "c53db2e5c642a5549c13f3c93a5b359fed69bdb2621bf71a243a877ffcb95e6b"
},
"room-contour-close": {
@@ -46,7 +46,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "ccbc4fa08439a1d7a585a5b742035d048a8039fe4d023a48788f811884b6316c",
"sourceSha256": "edb4278c0d33b75e417cc0b2d83ac8a46f2be4cf0d2c46aed801c566d82b70bc",
"imageSha256": "5ca7f24642926072ccbc5bf48341c5310329918631b147c116f9c29e20e4927e"
},
"plan-context-tray": {
@@ -57,7 +57,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "ccbc4fa08439a1d7a585a5b742035d048a8039fe4d023a48788f811884b6316c",
"sourceSha256": "edb4278c0d33b75e417cc0b2d83ac8a46f2be4cf0d2c46aed801c566d82b70bc",
"imageSha256": "164b79fd9075a1425cffe3c85c88b689dfda90b79856bb4ed6655507b166b3e0"
},
"device-editor": {
@@ -68,8 +68,8 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "ccbc4fa08439a1d7a585a5b742035d048a8039fe4d023a48788f811884b6316c",
"imageSha256": "7a22a81e2abedf5a520bd36b7509d953cbfa9962b48c0ffbc233666a6215c04b"
"sourceSha256": "edb4278c0d33b75e417cc0b2d83ac8a46f2be4cf0d2c46aed801c566d82b70bc",
"imageSha256": "7c3e25534fc819c45431907616c3d523961505859ee68af27cce2daec9f04fb0"
},
"device-display-preview": {
"file": "06-device-display-preview.png",
@@ -79,8 +79,8 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "ccbc4fa08439a1d7a585a5b742035d048a8039fe4d023a48788f811884b6316c",
"imageSha256": "df3a54395c83bffd7a161f1fb3de0143e12e76567535aedf9883873ead74df91"
"sourceSha256": "edb4278c0d33b75e417cc0b2d83ac8a46f2be4cf0d2c46aed801c566d82b70bc",
"imageSha256": "f6014caed7b7d28790b8548996ba09d806a4e4d51fbb7e8d3fb3c582ebe49167"
},
"background-editor": {
"file": "07-background-editor.png",
@@ -90,7 +90,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "ccbc4fa08439a1d7a585a5b742035d048a8039fe4d023a48788f811884b6316c",
"sourceSha256": "edb4278c0d33b75e417cc0b2d83ac8a46f2be4cf0d2c46aed801c566d82b70bc",
"imageSha256": "d7cfe70551d9260169df8efd832e32ed7df99c7f60b55d1fd4a8322bd4c47175"
},
"room-card": {
@@ -101,7 +101,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "ccbc4fa08439a1d7a585a5b742035d048a8039fe4d023a48788f811884b6316c",
"sourceSha256": "edb4278c0d33b75e417cc0b2d83ac8a46f2be4cf0d2c46aed801c566d82b70bc",
"imageSha256": "176abba71d41cfb045a33f82a794d9fbb2a5d3c48e6df66e6ec3a8e448311b11"
},
"device-info": {
@@ -112,7 +112,7 @@
},
"theme": "dark",
"language": "en",
"sourceSha256": "ccbc4fa08439a1d7a585a5b742035d048a8039fe4d023a48788f811884b6316c",
"sourceSha256": "edb4278c0d33b75e417cc0b2d83ac8a46f2be4cf0d2c46aed801c566d82b70bc",
"imageSha256": "2199ed88b215bf2bff63bf665028f78b2aa6a92c3032b803931790f2ef071893"
}
}
+18 -55
View File
@@ -513,7 +513,7 @@ const unionRect = (a: Rect, b: Rect): Rect => {
return { x, y, w: Math.max(a.x + a.w, b.x + b.w) - x, h: Math.max(a.y + a.h, b.y + b.h) - y };
};
type MarkupTool = 'select' | 'draw' | 'partition' | 'column' | 'merge' | 'split' | 'resize' | 'opening' | 'boundary' | 'wallthick' | 'delroom';
type MarkupTool = 'select' | 'draw' | 'column' | 'merge' | 'split' | 'resize' | 'opening' | 'boundary' | 'wallthick' | 'delroom';
type RoomFillFrame = {
byRoom: Map<RoomCfg, ResolvedRoomFill | null>;
byId: Map<string, ResolvedRoomFill | null>;
@@ -544,7 +544,7 @@ type WallFaceBatch = {
*/
type GlowClipGeometry = { lit: string[] };
const MARKUP_TOOLS = new Set<MarkupTool>([
'select', 'draw', 'partition', 'column', 'merge', 'split', 'resize',
'select', 'draw', 'column', 'merge', 'split', 'resize',
'opening', 'boundary', 'wallthick', 'delroom',
]);
/** Warm viewport is page-memory, so it may contain a tool name from the old bundle. */
@@ -2238,7 +2238,7 @@ class HouseplanCard extends LitElement {
}
// A crash-safe segment is a real geometry command, but Undo keeps the
// surviving draft active so the visible contract remains “one point back”.
if ((this._tool === 'draw' || this._tool === 'partition') && this._path.length) {
if (this._tool === 'draw' && this._path.length) {
if (this._activeDraftId && this._path.length > 1) this._undoActiveDraftPoint();
else this._undoPoint();
return;
@@ -2267,11 +2267,6 @@ class HouseplanCard extends LitElement {
this._undoPoint();
return;
}
if (this._tool === 'partition' && this._path.length) {
e.preventDefault();
this._undoPoint();
return;
}
if (this._physicalSel) {
e.preventDefault();
this._physicalSel = null;
@@ -2320,7 +2315,7 @@ class HouseplanCard extends LitElement {
return;
}
if (this._tool === 'opening' || this._tool === 'wallthick' || this._tool === 'delroom'
|| this._tool === 'partition' || this._tool === 'column') {
|| this._tool === 'column') {
e.preventDefault();
if (this._tool === 'opening') this._clearOpeningPlacement(true);
this._tool = 'draw';
@@ -5359,7 +5354,7 @@ class HouseplanCard extends LitElement {
if (this._tool === 'opening') {
this._cursorPt = null;
this._clearOpeningPlacement(false);
} else if (this._tool === 'draw' || this._tool === 'partition') {
} else if (this._tool === 'draw') {
this._clearPlanSnapHover();
}
const pts = [...this._pointers.values()];
@@ -5426,7 +5421,7 @@ class HouseplanCard extends LitElement {
if (this._tool === 'opening') {
this._cursorPt = null;
this._clearOpeningPlacement(false);
} else if (this._tool === 'draw' || this._tool === 'partition') {
} else if (this._tool === 'draw') {
this._clearPlanSnapHover();
}
const pts = [...this._pointers.values()];
@@ -5447,7 +5442,7 @@ class HouseplanCard extends LitElement {
if (this._tool === 'opening') {
this._cursorPt = null;
this._clearOpeningPlacement(false);
} else if (this._tool === 'draw' || this._tool === 'partition') {
} else if (this._tool === 'draw') {
this._clearPlanSnapHover();
}
}
@@ -5484,7 +5479,7 @@ class HouseplanCard extends LitElement {
if (this._tool === 'opening') {
this._cursorPt = null;
this._clearOpeningPlacement(false);
} else if (this._tool === 'draw' || this._tool === 'partition') {
} else if (this._tool === 'draw') {
this._clearPlanSnapHover();
}
}
@@ -6361,7 +6356,7 @@ class HouseplanCard extends LitElement {
}
private get _activePlanSnapCandidate(): PlanSnapCandidate | null {
if (!this._markup || (this._tool !== 'draw' && this._tool !== 'partition')) return null;
if (!this._markup || this._tool !== 'draw') return null;
const hover = this._planSnapHover;
if (!hover) return null;
const snapshot = this._planSnapGeometrySnapshot();
@@ -6598,7 +6593,7 @@ class HouseplanCard extends LitElement {
if (this._tool === 'opening') {
this._cursorPt = null;
this._clearOpeningPlacement(false);
} else if (this._tool === 'draw' || this._tool === 'partition') {
} else if (this._tool === 'draw') {
this._clearPlanSnapHover();
}
const viewportGestureEnded = !!this._pinchStart || !!this._panStart;
@@ -6831,10 +6826,6 @@ class HouseplanCard extends LitElement {
this._splitClick(raw);
return;
}
if (this._tool === 'partition') {
this._partitionClick(raw, ev.shiftKey);
return;
}
if (this._tool === 'column') {
this._columnClick(raw);
return;
@@ -7140,29 +7131,6 @@ class HouseplanCard extends LitElement {
this._roomDialog = true;
}
private _partitionClick(raw: number[], lock45: boolean): void {
const pt = this._resolvePlanDrawPoint(raw, lock45).point;
if (!this._path.length) { this._path = [pt]; return; }
const a = this._path[0];
if (this._samePt(a, pt)) return;
const cm = this._drawWallCm;
if (cm == null) { this._showPhysicalRange(100); return; }
if (!this._curSpaceCfg || this._limitReached('partition')) return;
const before = this._geometrySnapshot();
const sp = this._curSpaceCfg as any;
sp.partitions ||= [];
const id = 'partition-' + Date.now().toString(36);
sp.partitions.push({ id, a: [a[0] / NORM_W, a[1] / NORM_W],
b: [pt[0] / NORM_W, pt[1] / NORM_W], cm });
this._path = [];
this._activeDraftId = null;
this._draftSegmentCms = [];
this._closingWallCm = null;
this._clearPlanSnapHover();
this._recordGeometry(this._t('history.partition_add'), before);
this._saveConfig();
}
private _columnClick(raw: number[]): void {
const center = this._snap(raw);
const cm = this._drawWallCm;
@@ -9556,7 +9524,6 @@ class HouseplanCard extends LitElement {
this._drawWallField = (e.target as HTMLInputElement).value;
}}
title=${this._t(this._tool === 'draw' ? 'markup.draw_wall_title'
: this._tool === 'partition' ? 'physical.partition_size_title'
: 'physical.column_size_title')} />
<span class="opl">${this._t(this._imperial ? 'wallthick.unit_in' : 'wallthick.unit_cm')}</span>
<span class="rangehint">${this._t('physical.allowed_range', {
@@ -9594,9 +9561,8 @@ class HouseplanCard extends LitElement {
};
}
const hasThickness = this._tool === 'draw' || this._tool === 'partition' || this._tool === 'column';
const hintKey = this._tool === 'partition' ? 'markup.hint_partition'
: this._tool === 'column' ? 'markup.hint_column'
const hasThickness = this._tool === 'draw' || this._tool === 'column';
const hintKey = this._tool === 'column' ? 'markup.hint_column'
: this._tool === 'resize' ? 'markup.hint_resize'
: this._tool === 'wallthick' ? 'markup.hint_wallthick'
: this._tool === 'boundary' ? this._boundaryHintKey
@@ -9613,7 +9579,6 @@ class HouseplanCard extends LitElement {
kind: operation ? 'operation' : 'tool',
ariaLabel: this._t('editor.tool_options', {
tool: this._t((this._tool === 'draw' ? 'markup.add'
: this._tool === 'partition' ? 'markup.partition'
: this._tool === 'column' ? 'markup.column'
: this._tool === 'resize' ? 'markup.resize'
: this._tool === 'wallthick' ? 'markup.wallthick'
@@ -11905,8 +11870,7 @@ class HouseplanCard extends LitElement {
this._cursorPt = this._svgPoint(ev);
return;
}
const architectural = (this._tool === 'draw' || this._tool === 'partition')
&& !this._contourClosed;
const architectural = this._tool === 'draw' && !this._contourClosed;
const cutting = this._tool === 'split' && !!this._splitSel?.pts?.length;
if (!architectural && !cutting) return;
const raw = this._svgPoint(ev);
@@ -17199,8 +17163,7 @@ class HouseplanCard extends LitElement {
/** Where the live measurement starts: the last outline point, or the first split point. */
private get _measureAnchor(): number[] | null {
if (!this._markup || !this._cursorPt) return null;
if ((this._tool === 'draw' || this._tool === 'partition')
&& this._path.length && !this._contourClosed)
if (this._tool === 'draw' && this._path.length && !this._contourClosed)
return this._path[this._path.length - 1];
if (this._tool === 'split' && this._splitSel?.pts?.length)
return this._splitSel.pts[this._splitSel.pts.length - 1];
@@ -17920,7 +17883,7 @@ class HouseplanCard extends LitElement {
}
private _renderPlanSnapOverlay(): TemplateResult {
if (!this._markup || (this._tool !== 'draw' && this._tool !== 'partition')) {
if (!this._markup || this._tool !== 'draw') {
return svg`` as unknown as TemplateResult;
}
const geometry = this._planSnapGeometrySnapshot().value;
@@ -18036,9 +17999,9 @@ class HouseplanCard extends LitElement {
const path = this._path;
const g = this._gridPitch;
const view = this._viewOr(this._baseVb());
const drawCm = this._tool === 'draw' || this._tool === 'partition' ? this._drawWallCm : null;
const drawCm = this._tool === 'draw' ? this._drawWallCm : null;
const previewPts = (() => {
if ((this._tool !== 'draw' && this._tool !== 'partition') || !path.length || !(drawCm != null && drawCm > 0)) return null;
if (this._tool !== 'draw' || !path.length || !(drawCm != null && drawCm > 0)) return null;
if (this._contourClosed) return path;
if (this._cursorPt) return [...path, this._cursorPt];
return path.length >= 2 ? path : null;
@@ -18088,7 +18051,7 @@ class HouseplanCard extends LitElement {
${path.length > 1
? svg`<polyline class="pathline" points="${path.map((p) => p.join(',')).join(' ')}"></polyline>`
: nothing}
${path.length && this._cursorPt && (this._tool === 'draw' || this._tool === 'partition') && !this._contourClosed
${path.length && this._cursorPt && this._tool === 'draw' && !this._contourClosed
? svg`<line class="preview" x1="${path[path.length - 1][0]}" y1="${path[path.length - 1][1]}"
x2="${this._cursorPt[0]}" y2="${this._cursorPt[1]}"></line>`
: nothing}
-3
View File
@@ -13,12 +13,10 @@
"color_picker.invalid_hex": "Enter a 3- or 6-digit hex color",
"btn.properties": "Properties",
"btn.keep_as_walls": "Keep as walls",
"title.markup_partition": "Partition: draw one independent physical wall",
"title.markup_select": "Select and edit walls, columns and saved outlines",
"title.markup_column": "Column: click a grid point to place a square column",
"markup.partition": "Partition",
"markup.column": "Column",
"markup.hint_partition": "two clicks draw one partition · Shift locks to 45°",
"markup.hint_column": "click a grid point to place a square column",
"history.draft_segment": "Add room-draft segment",
"history.draft_merge": "Join unfinished room outlines",
@@ -47,7 +45,6 @@
"physical.allowed_range": "Allowed: 1–{max} {unit}",
"physical.delete_segment": "Delete segment",
"physical.delete_draft": "Delete entire outline",
"physical.partition_size_title": "Thickness of the partition created by the second click (1–100 cm).",
"physical.column_size_title": "Outer side of the square column placed by the click (1–150 cm).",
"card.title": "House plan",
"count.devices": "{n} dev.",
-3
View File
@@ -13,12 +13,10 @@
"color_picker.invalid_hex": "Введите цвет HEX из 3 или 6 цифр",
"btn.properties": "Свойства",
"btn.keep_as_walls": "Оставить стенами",
"title.markup_partition": "Перегородка: нарисуйте одну независимую физическую стену",
"title.markup_select": "Выбор и редактирование стен, колонн и сохранённых контуров",
"title.markup_column": "Колонна: кликните точку сетки для квадратной колонны",
"markup.partition": "Перегородка",
"markup.column": "Колонна",
"markup.hint_partition": "два клика рисуют одну перегородку · Shift фиксирует шаг 45°",
"markup.hint_column": "кликните точку сетки, чтобы поставить квадратную колонну",
"history.draft_segment": "Добавление сегмента черновика комнаты",
"history.draft_merge": "Соединение незавершённых контуров комнаты",
@@ -47,7 +45,6 @@
"physical.allowed_range": "Допустимо: 1–{max} {unit}",
"physical.delete_segment": "Удалить сегмент",
"physical.delete_draft": "Удалить весь контур",
"physical.partition_size_title": "Толщина перегородки, создаваемой вторым кликом (1–100 см).",
"physical.column_size_title": "Внешняя сторона квадратной колонны, создаваемой кликом (1–150 см).",
"card.title": "План дома",
"count.devices": "{n} устр.",
+1 -1
View File
@@ -69,7 +69,7 @@ test('golden matrix has stable unique ids and bounded comparison thresholds', ()
assert.equal(scenario.openHelp, scenario.helpTextRegion.key, scenario.id);
}
if (scenario.planSnap) {
assert.equal(['draw', 'partition'].includes(scenario.planSnap.tool), true, scenario.id);
assert.equal(scenario.planSnap.tool, 'draw', scenario.id);
assert.equal(['endpoint', 'line'].includes(scenario.planSnap.expectedKind), true, scenario.id);
assert.equal(scenario.mode, 'plan', scenario.id);
assert.equal(scenario.capture, 'page', scenario.id);
+34
View File
@@ -0,0 +1,34 @@
import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
import test from 'node:test';
const cardSource = readFileSync(new URL('../src/houseplan-card.ts', import.meta.url), 'utf8');
const en = JSON.parse(readFileSync(new URL('../src/i18n/en.json', import.meta.url), 'utf8'));
const ru = JSON.parse(readFileSync(new URL('../src/i18n/ru.json', import.meta.url), 'utf8'));
test('runtime exposes only the unified Walls tool state', () => {
const toolType = cardSource.match(/type MarkupTool = ([^;]+);/)?.[1] || '';
const toolSet = cardSource.match(/const MARKUP_TOOLS = new Set<MarkupTool>\(\[([\s\S]*?)\]\);/)?.[1] || '';
assert.doesNotMatch(toolType, /['"]partition['"]/);
assert.doesNotMatch(toolSet, /['"]partition['"]/);
assert.doesNotMatch(cardSource, /\b_partitionClick\b/);
assert.doesNotMatch(cardSource, /this\._tool\s*(?:===|!==)\s*['"]partition['"]/);
assert.doesNotMatch(cardSource, /['"]partition['"]\s*(?:===|!==)\s*this\._tool/);
});
test('only old tool copy is removed while persisted partition copy remains', () => {
for (const locale of [en, ru]) {
for (const key of [
'title.markup_partition',
'markup.hint_partition',
'physical.partition_size_title',
]) assert.equal(key in locale, false, key);
for (const key of [
'markup.partition',
'physical.partition_properties',
'history.partition_add',
]) assert.equal(typeof locale[key], 'string', key);
}
});