mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-28 19:01:34 +00:00
fix: show honest new-space display defaults
Issue: #204 User-Visible: yes
This commit is contained in:
committed by
claude[bot]
parent
4c8ba981e7
commit
6f89002e3a
File diff suppressed because one or more lines are too long
@@ -0,0 +1,103 @@
|
||||
import { launch, checkAll, finish } from './serve.mjs';
|
||||
|
||||
const { page, browser } = await launch();
|
||||
const res = await page.evaluate(async () => {
|
||||
const out = {};
|
||||
const c = window.__card;
|
||||
const sr = () => c.shadowRoot || c.renderRoot;
|
||||
const state = () => ({
|
||||
source: c._spaceDialog?.source,
|
||||
showBorders: c._spaceDialog?.showBorders,
|
||||
showNames: c._spaceDialog?.showNames,
|
||||
displayTouched: c._spaceDialog?.displayTouched,
|
||||
});
|
||||
const choose = async (source) => {
|
||||
const radios = [...sr().querySelectorAll('hp-dialog input[name="plansrc"]')];
|
||||
radios[source === 'file' ? 0 : 1].click();
|
||||
await c.updateComplete;
|
||||
};
|
||||
const toggle = async (key) => {
|
||||
const label = [...sr().querySelectorAll('hp-dialog label.srcrow')]
|
||||
.find((row) => row.textContent.includes(c._t(key)));
|
||||
label.querySelector('input[type="checkbox"], ha-switch').click();
|
||||
await c.updateComplete;
|
||||
};
|
||||
|
||||
c._openSpaceDialog('create'); await c.updateComplete;
|
||||
out.freshFile = state();
|
||||
await choose('draw');
|
||||
out.untouchedDraw = state();
|
||||
await choose('file');
|
||||
out.untouchedFileAgain = state();
|
||||
await choose('draw');
|
||||
await toggle('space.show_borders');
|
||||
out.mixedTouched = state();
|
||||
await choose('file');
|
||||
out.mixedFile = state();
|
||||
await choose('draw');
|
||||
out.mixedDrawAgain = state();
|
||||
|
||||
c._spaceDialog = { ...c._spaceDialog, title: 'Mixed display defaults' };
|
||||
await c._saveSpaceDialog(); await c.updateComplete;
|
||||
const mixed = c._serverCfg.spaces.find((s) => s.title === 'Mixed display defaults');
|
||||
out.savedMixed = [mixed?.settings?.show_borders, mixed?.settings?.show_names];
|
||||
c._openSpaceDialog('edit', mixed.id); await c.updateComplete;
|
||||
out.reopenedMixed = state();
|
||||
await choose('file');
|
||||
out.editSourcePreservesPair = state();
|
||||
|
||||
const beforeCancel = JSON.stringify(c._serverCfg);
|
||||
c._spaceDialog = null; await c.updateComplete;
|
||||
c._openSpaceDialog('create'); await c.updateComplete;
|
||||
await choose('draw');
|
||||
await toggle('space.show_names');
|
||||
c._spaceDialog = null; await c.updateComplete;
|
||||
out.cancelDidNotWrite = JSON.stringify(c._serverCfg) === beforeCancel;
|
||||
c._openSpaceDialog('create'); await c.updateComplete;
|
||||
out.freshAfterCancel = state();
|
||||
|
||||
c._spaceDialog = null;
|
||||
c._importQueue = ['Floor one', 'Floor two'];
|
||||
c._importTotal = 2;
|
||||
c._openNextImport(); await c.updateComplete;
|
||||
out.onboardingFirst = { title: c._spaceDialog.title, ...state() };
|
||||
await choose('draw');
|
||||
await toggle('space.show_borders');
|
||||
c._skipImport(); await c.updateComplete;
|
||||
out.onboardingSecond = { title: c._spaceDialog.title, ...state() };
|
||||
|
||||
c._importQueue = [];
|
||||
c._importTotal = 0;
|
||||
c._spaceDialog = null;
|
||||
c._openSpaceDialog('create'); await c.updateComplete;
|
||||
const existingPlan = c._serverCfg.spaces.find((s) => s.plan_url)?.plan_url;
|
||||
c._spaceDialog = {
|
||||
...c._spaceDialog, title: 'File display defaults', planUrl: existingPlan || '/local/houseplan/plan.svg',
|
||||
};
|
||||
await c._saveSpaceDialog(); await c.updateComplete;
|
||||
const file = c._serverCfg.spaces.find((s) => s.title === 'File display defaults');
|
||||
out.savedFileDefaults = [file?.settings?.show_borders, file?.settings?.show_names];
|
||||
|
||||
return out;
|
||||
});
|
||||
|
||||
checkAll(res, {
|
||||
freshFile: { source: 'file', showBorders: false, showNames: false, displayTouched: false },
|
||||
untouchedDraw: { source: 'draw', showBorders: true, showNames: true, displayTouched: false },
|
||||
untouchedFileAgain: { source: 'file', showBorders: false, showNames: false, displayTouched: false },
|
||||
mixedTouched: { source: 'draw', showBorders: false, showNames: true, displayTouched: true },
|
||||
mixedFile: { source: 'file', showBorders: false, showNames: true, displayTouched: true },
|
||||
mixedDrawAgain: { source: 'draw', showBorders: false, showNames: true, displayTouched: true },
|
||||
savedMixed: [false, true],
|
||||
reopenedMixed: { source: 'draw', showBorders: false, showNames: true, displayTouched: true },
|
||||
editSourcePreservesPair: { source: 'file', showBorders: false, showNames: true, displayTouched: true },
|
||||
freshAfterCancel: { source: 'file', showBorders: false, showNames: false, displayTouched: false },
|
||||
onboardingFirst: {
|
||||
title: 'Floor one', source: 'file', showBorders: false, showNames: false, displayTouched: false,
|
||||
},
|
||||
onboardingSecond: {
|
||||
title: 'Floor two', source: 'file', showBorders: false, showNames: false, displayTouched: false,
|
||||
},
|
||||
savedFileDefaults: [false, false],
|
||||
});
|
||||
await finish(browser, res);
|
||||
@@ -62,7 +62,9 @@ const res = await page.evaluate(async () => {
|
||||
out.labelSaved = !!c._layout['rl_r1'];
|
||||
// 6) диалог: create + draw
|
||||
c._openSpaceDialog('create'); await c.updateComplete;
|
||||
c._spaceDialog = { ...c._spaceDialog, title: 'Attic', source: 'draw', orientation: 'square' };
|
||||
[...sr().querySelectorAll('input[name="plansrc"]')][1].click();
|
||||
await c.updateComplete;
|
||||
c._spaceDialog = { ...c._spaceDialog, title: 'Attic', orientation: 'square' };
|
||||
await c.updateComplete;
|
||||
out.saveEnabled = !sr().querySelector('hp-dialog .btn.on[disabled]');
|
||||
await c._saveSpaceDialog(); await c.updateComplete;
|
||||
|
||||
File diff suppressed because one or more lines are too long
Vendored
+76
-76
File diff suppressed because one or more lines are too long
@@ -2,6 +2,11 @@
|
||||
|
||||
## v1.65.0-beta.5 — 2026-08-19
|
||||
|
||||
- The new-space dialog now shows the defaults it will actually save: image
|
||||
spaces start with borders and names off, while switching to a hand-drawn
|
||||
plan visibly turns both on. After either switch is edited, changing the plan
|
||||
source preserves the complete user-selected pair; the Floors/Areas wizard
|
||||
follows the same rule ([#204](https://github.com/Matysh/houseplan-card/issues/204)).
|
||||
- Turning off **Show names** now removes room names completely in View, kiosk,
|
||||
the static card and hidden isometric mode, while Plan still exposes the
|
||||
existing draggable card for positioning. Re-enabling names restores its
|
||||
|
||||
@@ -8,6 +8,12 @@
|
||||
|
||||
## v1.65.0-beta.5 — 2026-08-19
|
||||
|
||||
- Диалог нового пространства теперь показывает именно те значения, которые
|
||||
сохранит: для плана с изображением границы и названия изначально выключены,
|
||||
а при переходе к ручному плану оба переключателя видимо включаются. После
|
||||
изменения любого из них смена источника сохраняет всю выбранную пару; мастер
|
||||
этажей/зон следует тому же правилу
|
||||
([#204](https://github.com/Matysh/houseplan-card/issues/204)).
|
||||
- Выключение **«Показывать названия»** теперь полностью убирает названия комнат
|
||||
из Просмотра, киоска, статической карточки и скрытой изометрии, а редактор
|
||||
Плана по-прежнему временно показывает существующую карточку для настройки
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ metadata). Only an explicit owner-approved emergency hotfix may skip this gate.
|
||||
| Item | State |
|
||||
|---|---|
|
||||
| Version | **v1.65.0-beta.5** everywhere (manifest, const.py, package.json, CARD_VERSION) — prerelease candidate for the current `S8-merged` queue |
|
||||
| Current local cycle | v1.65.0-beta.5 candidate carries #176, #186, #195, #200, #201 and #203: independent-wall openings preserve their physical jamb margin, room labels keep View/Plan geometry and obey the name-visibility switch, atomic wall stretches inherit the intended thickness, the editor close target is easier to hit, and unreachable legacy Partition tool-state is removed. Publication waits for a green exact-SHA Validate and fail-closed prerelease preflight; v1.65.0-beta.4 is published and stable v1.64.0 remains unchanged. |
|
||||
| Current local cycle | v1.65.0-beta.5 candidate carries #176, #186, #195, #200, #201 and #203; issue branch #204 additionally makes new-space File/Draw border/name defaults visible and preserves a user-touched pair across source switches. Independent-wall openings preserve their physical jamb margin, room labels keep View/Plan geometry and obey the name-visibility switch, atomic wall stretches inherit the intended thickness, the editor close target is easier to hit, and unreachable legacy Partition tool-state is removed. Publication waits for a green exact-SHA Validate and fail-closed prerelease preflight; v1.65.0-beta.4 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) |
|
||||
|
||||
@@ -2011,6 +2011,25 @@ require hands on real hardware — they remain for the human pass.
|
||||
- [ ] **A non-admin never sees it**: the button follows the same rule as the
|
||||
per-space gear (`_canEdit`) [manual, needs a non-admin HA user]
|
||||
|
||||
## Honest new-space display defaults (#204, dev, unreleased)
|
||||
|
||||
- [ ] **The dialog and Save agree**: open a new space. File begins with room
|
||||
borders/names `false/false`; before either control is touched, switching
|
||||
to Draw shows `true/true` and switching back restores `false/false`.
|
||||
Saving either source and reopening it yields the exact visible pair
|
||||
[auto: `space-dialog.test`, `smoke_space_create_display_defaults`,
|
||||
`smoke_space_settings`].
|
||||
- [ ] **One touch protects both choices**: on Draw change either display
|
||||
switch, including the mixed `true/false` and `false/true` cases. Further
|
||||
File ↔ Draw switches change only the source; Save never silently restores
|
||||
`true/true` [auto: `space-dialog.test`,
|
||||
`smoke_space_create_display_defaults`; mutation:
|
||||
`space-create-hidden-display-override`].
|
||||
- [ ] **Draft state does not leak**: Cancel and a fresh Create return to File
|
||||
`false/false`. In Floors/Areas onboarding every next floor also starts
|
||||
clean and cannot inherit the preceding floor's touched state
|
||||
[auto: `smoke_space_create_display_defaults`].
|
||||
|
||||
## Coming back to the tab (docs/WARM-REMOUNT.md, dev, unreleased)
|
||||
|
||||
- [ ] **A quick return does not flash**: leave the browser tab or minimise the
|
||||
|
||||
@@ -262,6 +262,12 @@ desktop: для точного рисования, Resize, модификато
|
||||
| Без изображения | План рисуется непосредственно в House Plan | Бумага формируется по контурам комнат; границы и названия удобно включить сразу |
|
||||
| Импорт этажей HA | В HA уже создан реестр этажей | Мастер создаёт пространства по очереди; любой этаж можно пропустить |
|
||||
|
||||
Новое пространство с изображением открывается с выключенными границами и
|
||||
названиями. При выборе варианта **«Без изображения»** оба переключателя сразу
|
||||
включаются — это видимый default ручного плана. Если изменить хотя бы один из
|
||||
них, последующая смена источника больше не сбрасывает ни один выбор. В мастере
|
||||
этажей каждый следующий этаж получает собственные чистые defaults.
|
||||
|
||||
### Настройки пространства
|
||||
|
||||
| Раздел | Настройка | Результат |
|
||||
|
||||
@@ -476,6 +476,18 @@ export const MUTANTS = [
|
||||
replace: ' const areaLinkInteractive = true;',
|
||||
}],
|
||||
},
|
||||
{
|
||||
id: 'space-create-hidden-display-override',
|
||||
guard: 'node demo/smoke_space_create_display_defaults.mjs',
|
||||
because: 'Save must persist the two booleans visibly chosen for a new hand-drawn space; '
|
||||
+ 'the production smoke saves and reopens a mixed pair so the old hidden true/true override cannot return',
|
||||
patches: [{
|
||||
file: 'src/houseplan-card.ts',
|
||||
find: ' show_borders: d.showBorders,\n show_names: d.showNames,',
|
||||
replace: " show_borders: d.source === 'draw' && d.mode === 'create' ? true : d.showBorders,\n"
|
||||
+ " show_names: d.source === 'draw' && d.mode === 'create' ? true : d.showNames,",
|
||||
}],
|
||||
},
|
||||
];
|
||||
|
||||
// --- механика ---------------------------------------------------------------
|
||||
|
||||
+16
-12
@@ -84,6 +84,9 @@ import {
|
||||
resolveInitialSpace, settleBestEffort, type InitialSpaceSelection,
|
||||
} from './initial-load';
|
||||
import { selectActiveSpaceModel, selectSpaceModelById } from './space-model-selection';
|
||||
import {
|
||||
initialSpaceDisplayDraft, switchSpacePlanSource, touchSpaceDisplay,
|
||||
} from './space-dialog';
|
||||
import { mdiHomeCityOutline } from '@mdi/js';
|
||||
import {
|
||||
Affine, applyAffine, readVacTelemetry,
|
||||
@@ -1593,6 +1596,8 @@ class HouseplanCard extends LitElement {
|
||||
source: 'file' | 'draw'; // draw = no background image, hand-drawn rooms
|
||||
showBorders: boolean;
|
||||
showNames: boolean;
|
||||
/** Create-only source-default guard; never persisted. Edit starts touched. */
|
||||
displayTouched: boolean;
|
||||
hideDecor: boolean; // the decorative layer is not drawn outside its editor
|
||||
hideOpenings: boolean; // opening symbols are not drawn outside the plan editor
|
||||
roomColor: string;
|
||||
@@ -13210,6 +13215,7 @@ class HouseplanCard extends LitElement {
|
||||
mode, spaceId, title: sp.title, planUrl: sp.plan_url || null, planFile: null,
|
||||
source: sp.plan_url ? 'file' : 'draw',
|
||||
showBorders: disp.showBorders, showNames: disp.showNames,
|
||||
displayTouched: true,
|
||||
hideDecor: disp.hideDecor, hideOpenings: disp.hideOpenings,
|
||||
roomColor: disp.color, roomOpacity: disp.opacity,
|
||||
// `none` is a legacy space token. The current editor represents its
|
||||
@@ -13232,8 +13238,7 @@ class HouseplanCard extends LitElement {
|
||||
} else {
|
||||
this._spaceDialog = {
|
||||
mode, title: '', planUrl: null, planFile: null,
|
||||
source: 'file',
|
||||
showBorders: false, showNames: false,
|
||||
...initialSpaceDisplayDraft(),
|
||||
hideDecor: false, hideOpenings: false,
|
||||
roomColor: DEFAULT_ROOM_COLOR, roomOpacity: DEFAULT_ROOM_OPACITY, fillMode: 'custom',
|
||||
// `custom` replaces the old `none` choice in the editor, but creating
|
||||
@@ -13469,12 +13474,12 @@ class HouseplanCard extends LitElement {
|
||||
delete sp.plan_x; delete sp.plan_y; delete sp.plan_scale;
|
||||
delete sp.plan_scale_x; delete sp.plan_scale_y; delete sp.plan_angle;
|
||||
}
|
||||
// per-space display settings; hand-drawn spaces get borders+names on by default
|
||||
const draw = d.source === 'draw';
|
||||
// Persist exactly what the dialog showed. Source defaults are projected
|
||||
// visibly while editing the create state, never hidden here at Save.
|
||||
sp.settings = {
|
||||
...(sp.settings || {}),
|
||||
show_borders: draw && d.mode === 'create' ? true : d.showBorders,
|
||||
show_names: draw && d.mode === 'create' ? true : d.showNames,
|
||||
show_borders: d.showBorders,
|
||||
show_names: d.showNames,
|
||||
// written only when ON: a plan that never hid anything stores nothing
|
||||
hide_decor: d.hideDecor || undefined,
|
||||
hide_openings: d.hideOpenings || undefined,
|
||||
@@ -13607,8 +13612,7 @@ class HouseplanCard extends LitElement {
|
||||
if (title === undefined) return;
|
||||
this._spaceDialog = {
|
||||
mode: 'create', title, planUrl: null, planFile: null,
|
||||
source: 'file',
|
||||
showBorders: false, showNames: false,
|
||||
...initialSpaceDisplayDraft(),
|
||||
hideDecor: false, hideOpenings: false,
|
||||
roomColor: DEFAULT_ROOM_COLOR, roomOpacity: DEFAULT_ROOM_OPACITY, fillMode: 'custom',
|
||||
customFill: null,
|
||||
@@ -19518,7 +19522,7 @@ class HouseplanCard extends LitElement {
|
||||
<label>${this._t('space.plan_label')}</label>
|
||||
<label class="srcrow">
|
||||
<input type="radio" name="plansrc" .checked=${d.source === 'file'}
|
||||
@change=${() => (this._spaceDialog = { ...d, source: 'file' })} />
|
||||
@change=${() => (this._spaceDialog = switchSpacePlanSource(d, 'file'))} />
|
||||
<span>${this._t('space.source_file')}</span>
|
||||
</label>
|
||||
${d.source === 'file'
|
||||
@@ -19545,7 +19549,7 @@ class HouseplanCard extends LitElement {
|
||||
: nothing}
|
||||
<label class="srcrow">
|
||||
<input type="radio" name="plansrc" .checked=${d.source === 'draw'}
|
||||
@change=${() => (this._spaceDialog = { ...d, source: 'draw' })} />
|
||||
@change=${() => (this._spaceDialog = switchSpacePlanSource(d, 'draw'))} />
|
||||
<span>${this._t('space.source_draw')}</span>
|
||||
</label>
|
||||
|
||||
@@ -19565,11 +19569,11 @@ class HouseplanCard extends LitElement {
|
||||
|
||||
<label class="dispsection">${this._t('space.display_section')}</label>
|
||||
<label class="srcrow">
|
||||
${this._boolInput(d.showBorders, (v) => (this._spaceDialog = { ...d, showBorders: v }))}
|
||||
${this._boolInput(d.showBorders, (v) => (this._spaceDialog = touchSpaceDisplay(d, 'showBorders', v)))}
|
||||
<span>${this._t('space.show_borders')}</span>
|
||||
</label>
|
||||
<label class="srcrow">
|
||||
${this._boolInput(d.showNames, (v) => (this._spaceDialog = { ...d, showNames: v }))}
|
||||
${this._boolInput(d.showNames, (v) => (this._spaceDialog = touchSpaceDisplay(d, 'showNames', v)))}
|
||||
<span>${this._t('space.show_names')}</span>
|
||||
</label>
|
||||
<label class="srcrow">
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
export type SpacePlanSource = 'file' | 'draw';
|
||||
|
||||
export interface SpaceDisplayDraft {
|
||||
source: SpacePlanSource;
|
||||
showBorders: boolean;
|
||||
showNames: boolean;
|
||||
/** Ephemeral create-dialog guard; never persisted in the House Plan config. */
|
||||
displayTouched: boolean;
|
||||
}
|
||||
|
||||
/** Source-specific visible defaults for a fresh create/onboarding step. */
|
||||
export function initialSpaceDisplayDraft(source: SpacePlanSource = 'file'): SpaceDisplayDraft {
|
||||
const visible = source === 'draw';
|
||||
return {
|
||||
source,
|
||||
showBorders: visible,
|
||||
showNames: visible,
|
||||
displayTouched: false,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Project source defaults only while the owner has not changed either display
|
||||
* switch. Once touched, the pair is one user choice and must survive switches.
|
||||
*/
|
||||
export function switchSpacePlanSource<T extends SpaceDisplayDraft>(
|
||||
draft: T,
|
||||
source: SpacePlanSource,
|
||||
): T {
|
||||
if (draft.displayTouched) return { ...draft, source };
|
||||
const visible = source === 'draw';
|
||||
return { ...draft, source, showBorders: visible, showNames: visible };
|
||||
}
|
||||
|
||||
/** Changing either switch protects the complete pair from source projection. */
|
||||
export function touchSpaceDisplay<T extends SpaceDisplayDraft>(
|
||||
draft: T,
|
||||
field: 'showBorders' | 'showNames',
|
||||
value: boolean,
|
||||
): T {
|
||||
return { ...draft, [field]: value, displayTouched: true };
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
|
||||
import {
|
||||
initialSpaceDisplayDraft,
|
||||
switchSpacePlanSource,
|
||||
touchSpaceDisplay,
|
||||
} from '../test-build/space-dialog.js';
|
||||
|
||||
test('fresh create projects honest File and Draw display defaults', () => {
|
||||
const file = initialSpaceDisplayDraft();
|
||||
assert.deepEqual(file, {
|
||||
source: 'file', showBorders: false, showNames: false, displayTouched: false,
|
||||
});
|
||||
|
||||
const draw = switchSpacePlanSource(file, 'draw');
|
||||
assert.deepEqual(draw, {
|
||||
source: 'draw', showBorders: true, showNames: true, displayTouched: false,
|
||||
});
|
||||
assert.deepEqual(switchSpacePlanSource(draw, 'file'), file);
|
||||
});
|
||||
|
||||
test('touching either display switch preserves the complete mixed pair', () => {
|
||||
const draw = switchSpacePlanSource(initialSpaceDisplayDraft(), 'draw');
|
||||
const mixed = touchSpaceDisplay(draw, 'showBorders', false);
|
||||
assert.deepEqual(mixed, {
|
||||
source: 'draw', showBorders: false, showNames: true, displayTouched: true,
|
||||
});
|
||||
assert.deepEqual(switchSpacePlanSource(mixed, 'file'), {
|
||||
...mixed, source: 'file',
|
||||
});
|
||||
assert.deepEqual(switchSpacePlanSource(mixed, 'draw'), mixed);
|
||||
});
|
||||
|
||||
test('every supported Draw pair remains exact after a touched source round-trip', () => {
|
||||
for (const [showBorders, showNames] of [
|
||||
[false, false], [true, false], [false, true],
|
||||
]) {
|
||||
let draft = switchSpacePlanSource(initialSpaceDisplayDraft(), 'draw');
|
||||
draft = touchSpaceDisplay(draft, 'showBorders', showBorders);
|
||||
draft = touchSpaceDisplay(draft, 'showNames', showNames);
|
||||
const roundTrip = switchSpacePlanSource(switchSpacePlanSource(draft, 'file'), 'draw');
|
||||
assert.deepEqual(roundTrip, {
|
||||
source: 'draw', showBorders, showNames, displayTouched: true,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
test('source and display transitions are immutable and preserve unrelated fields', () => {
|
||||
const draft = { ...initialSpaceDisplayDraft(), title: 'Ground', cellCm: 7.5 };
|
||||
const switched = switchSpacePlanSource(draft, 'draw');
|
||||
const touched = touchSpaceDisplay(switched, 'showNames', false);
|
||||
|
||||
assert.notEqual(switched, draft);
|
||||
assert.notEqual(touched, switched);
|
||||
assert.deepEqual(draft, {
|
||||
source: 'file', showBorders: false, showNames: false, displayTouched: false,
|
||||
title: 'Ground', cellCm: 7.5,
|
||||
});
|
||||
assert.equal(touched.title, 'Ground');
|
||||
assert.equal(touched.cellCm, 7.5);
|
||||
});
|
||||
+1
-1
@@ -20,7 +20,7 @@
|
||||
"src/virtual-light-state.ts",
|
||||
"src/types.ts",
|
||||
"src/space-geometry.ts",
|
||||
"src/signing.ts", "src/initial-load.ts", "src/space-model-selection.ts",
|
||||
"src/signing.ts", "src/initial-load.ts", "src/space-model-selection.ts", "src/space-dialog.ts",
|
||||
"src/visual-continuity.ts", "src/mode-transition.ts",
|
||||
"src/render-device-snapshot.ts",
|
||||
"src/command-stack.ts",
|
||||
|
||||
Reference in New Issue
Block a user