mirror of
https://github.com/Matysh/houseplan-card
synced 2026-10-07 23:19:14 +00:00
File diff suppressed because one or more lines are too long
@@ -7,7 +7,8 @@
|
||||
* no recorded thickness — the shape a mid-edit toolbar field used to produce —
|
||||
* and then reads three things that used to disagree: the committed partitions,
|
||||
* what the Thickness tool reports for the same stretch, and what a resumed
|
||||
* legacy draft carries.
|
||||
* legacy draft carries. A missing live tail uses the current field; only an
|
||||
* internal historical gap inherits the preceding segment.
|
||||
*/
|
||||
import { launch, checkAll, finish } from './serve.mjs';
|
||||
const { page, browser } = await launch();
|
||||
@@ -39,8 +40,8 @@ const res = await page.evaluate(async () => {
|
||||
[100 + 12 * g, 100 + 6 * g],
|
||||
];
|
||||
|
||||
// Two recorded segments at 30 and 25 cm, then a third point whose thickness
|
||||
// is missing: exactly what a field left mid-edit produced before #234.
|
||||
// Two recorded segments at 30 and 25 cm, then a live third segment whose
|
||||
// record does not exist yet. The current field is 30 cm.
|
||||
c._drawWallField = '30';
|
||||
await upd();
|
||||
out.fieldAcceptedThirty = c._drawWallCm === 30;
|
||||
@@ -61,8 +62,8 @@ const res = await page.evaluate(async () => {
|
||||
out.storedThreeSegments = stored.length === 3;
|
||||
out.storedFirstThirty = stored[0] === 30;
|
||||
out.storedSecondTwentyFive = stored[1] === 25;
|
||||
// The gap inherits the previous segment, never the global default.
|
||||
out.gapInheritedPrevious = stored[2] === 25;
|
||||
// A live missing tail uses the current field, not the previous segment.
|
||||
out.liveTailUsesCurrentField = stored[2] === 30;
|
||||
out.nothingFellBackToFifteen = !stored.includes(15);
|
||||
|
||||
// The Thickness tool reads the same value it stored: the mismatch between
|
||||
|
||||
File diff suppressed because one or more lines are too long
Vendored
+2
-2
File diff suppressed because one or more lines are too long
+4
-2
@@ -59,8 +59,10 @@
|
||||
edited between segments — used to be previewed at the drawn value and saved at
|
||||
15 cm, and the plan only revealed it later when the Thickness tool reported a
|
||||
number nobody had chosen. Preview, both writers and the Thickness highlight now
|
||||
read one resolver, and a missing record inherits the previous segment of the
|
||||
same chain ([#234](https://github.com/Matysh/houseplan-card/issues/234)).
|
||||
read one resolver: a historical missing record inherits the previous segment,
|
||||
while the live rubber-band uses the current field value and therefore no longer
|
||||
changes thickness on click
|
||||
([#234](https://github.com/Matysh/houseplan-card/issues/234)).
|
||||
|
||||
- Wall hatching no longer changes density with the space's grid scale. The same
|
||||
15 cm wall used to look different on plans with different `cell_cm` — from a
|
||||
|
||||
@@ -69,8 +69,9 @@
|
||||
отрезками, — показывался с нарисованной толщиной, а сохранялся с 15 см, и
|
||||
расхождение обнаруживалось позже: инструмент «Толщина» подсвечивал значение,
|
||||
которого никто не выбирал. Теперь превью, оба пути записи и подсветка читают
|
||||
один резолвер, а пропуск наследует толщину предыдущего отрезка той же цепочки
|
||||
([#234](https://github.com/Matysh/houseplan-card/issues/234)).
|
||||
один резолвер: старый пропуск наследует толщину предыдущего отрезка, а live
|
||||
rubber-band использует текущее значение поля и больше не меняет толщину при
|
||||
клике ([#234](https://github.com/Matysh/houseplan-card/issues/234)).
|
||||
|
||||
- Плотность штриховки стен больше не зависит от масштаба пространства. Раньше
|
||||
одна и та же стена 15 см выглядела на планах с разным `cell_cm` по-разному —
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 345 KiB After Width: | Height: | Size: 342 KiB |
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"version": 1,
|
||||
"fixture": "synthetic-only",
|
||||
"sourceFingerprint": "967bdb37e447ec29a88d82707f5ff957e576be72fbe801445c6cbdec970e43eb",
|
||||
"sourceFingerprint": "2d8295e95a0721f11075dd0388ab4960c79b480ae199a381d3bbd1086e5e5b34",
|
||||
"captureScriptSha256": "34f2219790d46efd8250e7a1bd829cb8fc0b0547e1260635fefa52407551b41b",
|
||||
"command": "npm run build && node demo/docs/capture.mjs",
|
||||
"scenarios": {
|
||||
@@ -13,7 +13,7 @@
|
||||
},
|
||||
"theme": "dark",
|
||||
"language": "en",
|
||||
"sourceSha256": "967bdb37e447ec29a88d82707f5ff957e576be72fbe801445c6cbdec970e43eb",
|
||||
"sourceSha256": "2d8295e95a0721f11075dd0388ab4960c79b480ae199a381d3bbd1086e5e5b34",
|
||||
"imageSha256": "2885f96e348b15ab7c696e56e99bddcd9bb2ee94218a883e5a2155f45f5042aa"
|
||||
},
|
||||
"view-touch": {
|
||||
@@ -24,7 +24,7 @@
|
||||
},
|
||||
"theme": "dark",
|
||||
"language": "en",
|
||||
"sourceSha256": "967bdb37e447ec29a88d82707f5ff957e576be72fbe801445c6cbdec970e43eb",
|
||||
"sourceSha256": "2d8295e95a0721f11075dd0388ab4960c79b480ae199a381d3bbd1086e5e5b34",
|
||||
"imageSha256": "f62d8af3617c00a5e99511bd765980d2e27badf25047a1be34046b64195abe6c"
|
||||
},
|
||||
"space-create": {
|
||||
@@ -35,7 +35,7 @@
|
||||
},
|
||||
"theme": "dark",
|
||||
"language": "en",
|
||||
"sourceSha256": "967bdb37e447ec29a88d82707f5ff957e576be72fbe801445c6cbdec970e43eb",
|
||||
"sourceSha256": "2d8295e95a0721f11075dd0388ab4960c79b480ae199a381d3bbd1086e5e5b34",
|
||||
"imageSha256": "c33a7279165a4cec6fa6fadb6fd08cd967e082a17fe101ef442d27d36ae59b6b"
|
||||
},
|
||||
"room-contour-close": {
|
||||
@@ -46,8 +46,8 @@
|
||||
},
|
||||
"theme": "dark",
|
||||
"language": "en",
|
||||
"sourceSha256": "967bdb37e447ec29a88d82707f5ff957e576be72fbe801445c6cbdec970e43eb",
|
||||
"imageSha256": "4d63670c7bcca33da21a6cf17275786bed12e8422a50d02d4dabe59645f2cd82"
|
||||
"sourceSha256": "2d8295e95a0721f11075dd0388ab4960c79b480ae199a381d3bbd1086e5e5b34",
|
||||
"imageSha256": "3d22e1a2a96823e443833e40ea8b93747099b0fae1e9e19315d8789ae4604997"
|
||||
},
|
||||
"plan-context-tray": {
|
||||
"file": "05-plan-context-tray.png",
|
||||
@@ -57,7 +57,7 @@
|
||||
},
|
||||
"theme": "dark",
|
||||
"language": "en",
|
||||
"sourceSha256": "967bdb37e447ec29a88d82707f5ff957e576be72fbe801445c6cbdec970e43eb",
|
||||
"sourceSha256": "2d8295e95a0721f11075dd0388ab4960c79b480ae199a381d3bbd1086e5e5b34",
|
||||
"imageSha256": "a6c526fede11bc3503fd2384bd4a3f6afa008f481c5c47578f06cae6c81c6f9b"
|
||||
},
|
||||
"device-editor": {
|
||||
@@ -68,7 +68,7 @@
|
||||
},
|
||||
"theme": "dark",
|
||||
"language": "en",
|
||||
"sourceSha256": "967bdb37e447ec29a88d82707f5ff957e576be72fbe801445c6cbdec970e43eb",
|
||||
"sourceSha256": "2d8295e95a0721f11075dd0388ab4960c79b480ae199a381d3bbd1086e5e5b34",
|
||||
"imageSha256": "9585b59add4d35b5a6f028ce5b720ef1b77a3f8b19436d192cd1a0e6fc637264"
|
||||
},
|
||||
"device-display-preview": {
|
||||
@@ -79,7 +79,7 @@
|
||||
},
|
||||
"theme": "dark",
|
||||
"language": "en",
|
||||
"sourceSha256": "967bdb37e447ec29a88d82707f5ff957e576be72fbe801445c6cbdec970e43eb",
|
||||
"sourceSha256": "2d8295e95a0721f11075dd0388ab4960c79b480ae199a381d3bbd1086e5e5b34",
|
||||
"imageSha256": "cfc317da4628d079a116ff71311fbf06b1eb3b181928a5ea7ba888ab936a5e8b"
|
||||
},
|
||||
"background-editor": {
|
||||
@@ -90,7 +90,7 @@
|
||||
},
|
||||
"theme": "dark",
|
||||
"language": "en",
|
||||
"sourceSha256": "967bdb37e447ec29a88d82707f5ff957e576be72fbe801445c6cbdec970e43eb",
|
||||
"sourceSha256": "2d8295e95a0721f11075dd0388ab4960c79b480ae199a381d3bbd1086e5e5b34",
|
||||
"imageSha256": "d7cfe70551d9260169df8efd832e32ed7df99c7f60b55d1fd4a8322bd4c47175"
|
||||
},
|
||||
"room-card": {
|
||||
@@ -101,7 +101,7 @@
|
||||
},
|
||||
"theme": "dark",
|
||||
"language": "en",
|
||||
"sourceSha256": "967bdb37e447ec29a88d82707f5ff957e576be72fbe801445c6cbdec970e43eb",
|
||||
"sourceSha256": "2d8295e95a0721f11075dd0388ab4960c79b480ae199a381d3bbd1086e5e5b34",
|
||||
"imageSha256": "029a3e69ec647a8a370d99e6bb7f9225833c526739076022f6b52ba54bff30ea"
|
||||
},
|
||||
"device-info": {
|
||||
@@ -112,7 +112,7 @@
|
||||
},
|
||||
"theme": "dark",
|
||||
"language": "en",
|
||||
"sourceSha256": "967bdb37e447ec29a88d82707f5ff957e576be72fbe801445c6cbdec970e43eb",
|
||||
"sourceSha256": "2d8295e95a0721f11075dd0388ab4960c79b480ae199a381d3bbd1086e5e5b34",
|
||||
"imageSha256": "a06cbf83f09e2f67b3566d7c0b10e973db060c3d20786f26f74ada6a21937c3e"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,8 +50,22 @@ export const MUTANTS = [
|
||||
+ 'that silently replaced 30 cm with 15 cm in the stored plan (#234)',
|
||||
patches: [{
|
||||
file: 'src/wall-face-graph.ts',
|
||||
find: ' const cm = own ?? previous ?? fallbackTail;',
|
||||
replace: ' const cm = own ?? fallbackTail;',
|
||||
find: ' const inherited = previous ?? active ?? fallback;',
|
||||
replace: ' const inherited = active ?? fallback;',
|
||||
}],
|
||||
},
|
||||
{
|
||||
id: 'chain-thickness-live-tail-inherits-previous',
|
||||
guard: 'npx tsc -p tsconfig.test.json && node scripts/fix-test-build.mjs '
|
||||
+ '&& node --test --test-name-pattern="live rubber-band" '
|
||||
+ 'test/wall-face-graph.test.mjs',
|
||||
because: 'the last missing thickness belongs to the live rubber-band, so a field change '
|
||||
+ 'between clicks must preview the value that the click will commit instead of inheriting '
|
||||
+ 'the preceding segment (#234)',
|
||||
patches: [{
|
||||
file: 'src/wall-face-graph.ts',
|
||||
find: ' const liveTail = active ?? previous ?? fallback;',
|
||||
replace: ' const liveTail = previous ?? active ?? fallback;',
|
||||
}],
|
||||
},
|
||||
{
|
||||
|
||||
+10
-5
@@ -65,9 +65,11 @@ export function normalizeUnifiedWallTool(value: unknown): unknown {
|
||||
* the owner discovered it much later by hovering a wall. Two formulas for one
|
||||
* meaning always drift; there is exactly one here now.
|
||||
*
|
||||
* A missing record inherits the previous segment of the same chain, then the
|
||||
* toolbar field, then the default (owner's decision 2026-08-21): that is what
|
||||
* the person saw on screen while drawing, and a global default is not.
|
||||
* A missing record inside a committed chain inherits the previous segment,
|
||||
* then the toolbar field, then the default (owner's decision 2026-08-21).
|
||||
* The final missing record is different: it is the live rubber-band, so the
|
||||
* toolbar field must win before the previous segment. Otherwise changing the
|
||||
* field between clicks previews the old thickness and commits the new one.
|
||||
*
|
||||
* Strictly positive is the validity boundary. The previous `wallChainSegments`
|
||||
* accepted a recorded zero, which cannot be drawn through the UI (1..100 cm,
|
||||
@@ -89,12 +91,15 @@ export function chainSegmentCms(
|
||||
// ровно тем дублированием, которое задача и убирает. Невалидный default —
|
||||
// дефект вызывающего, поэтому он приводится к минимальной допустимой
|
||||
// толщине (1 см, docs/WALL-THICKNESS.md), а не к выдуманному значению.
|
||||
const fallbackTail = valid(activeCm) ?? valid(defaultCm) ?? 1;
|
||||
const active = valid(activeCm);
|
||||
const fallback = valid(defaultCm) ?? 1;
|
||||
const out: number[] = [];
|
||||
let previous: number | null = null;
|
||||
for (let i = 0; i < count; i++) {
|
||||
const own = valid(recorded?.[i]);
|
||||
const cm = own ?? previous ?? fallbackTail;
|
||||
const inherited = previous ?? active ?? fallback;
|
||||
const liveTail = active ?? previous ?? fallback;
|
||||
const cm = own ?? (i === count - 1 ? liveTail : inherited);
|
||||
out.push(cm);
|
||||
previous = cm;
|
||||
}
|
||||
|
||||
@@ -55,17 +55,25 @@ test('chainSegmentCms fills a gap from the previous segment, then the field (#23
|
||||
// потеряна. Раньше запись давала 15, превью — 30; теперь ответ один.
|
||||
assert.deepEqual(chainSegmentCms(3, [30, 30], 30, 15), [30, 30, 30]);
|
||||
// Дырка в середине наследует предыдущий, а не следующий и не дефолт.
|
||||
assert.deepEqual(chainSegmentCms(4, [30, undefined, 20], 12, 15), [30, 30, 20, 20]);
|
||||
assert.deepEqual(chainSegmentCms(3, [30, undefined, 20], 12, 15), [30, 30, 20]);
|
||||
// Нет ни одной записи — текущее поле.
|
||||
assert.deepEqual(chainSegmentCms(2, [], 22, 15), [22, 22]);
|
||||
// Нет и поля — дефолт вызывающего.
|
||||
assert.deepEqual(chainSegmentCms(2, [], null, 15), [15, 15]);
|
||||
});
|
||||
|
||||
test('chainSegmentCms gives the live rubber-band the current field value (#234)', () => {
|
||||
// The last missing record is not historical damage: it is the segment under
|
||||
// the cursor. A field change between clicks must be visible before commit.
|
||||
assert.deepEqual(chainSegmentCms(2, [12], 24, 15), [12, 24]);
|
||||
// With no active field, a missing tail remains a historical gap and inherits.
|
||||
assert.deepEqual(chainSegmentCms(3, [30, undefined], null, 15), [30, 30, 30]);
|
||||
});
|
||||
|
||||
test('chainSegmentCms treats only strictly positive records as valid (#234)', () => {
|
||||
// Прежняя wallChainSegments считала записанный 0 валидным; через UI ноль
|
||||
// недостижим (1..100 см), но в старом черновике лежать может.
|
||||
assert.deepEqual(chainSegmentCms(3, [0, 30, 0], 25, 15), [25, 30, 30]);
|
||||
assert.deepEqual(chainSegmentCms(3, [0, 30, 0], 25, 15), [25, 30, 25]);
|
||||
assert.deepEqual(chainSegmentCms(4, [NaN, -5, null, 'x'], 18, 15), [18, 18, 18, 18]);
|
||||
assert.deepEqual(chainSegmentCms(2, [Infinity, 40], 18, 15), [18, 40]);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user