feat: add hidden isometric stage 2

Issue: #122
User-Visible: no
This commit is contained in:
Sergey Matyunin
2026-08-14 00:43:13 +03:00
parent 4c73e2ccdb
commit 42b3f44c4a
19 changed files with 1051 additions and 53 deletions
+24 -2
View File
@@ -4,6 +4,8 @@ import { readFileSync } from 'node:fs';
const card = readFileSync(new URL('../src/houseplan-card.ts', import.meta.url), 'utf8');
const styles = readFileSync(new URL('../src/styles.ts', import.meta.url), 'utf8');
const openings = readFileSync(new URL('../src/iso-openings.ts', import.meta.url), 'utf8');
const packageJson = readFileSync(new URL('../package.json', import.meta.url), 'utf8');
const spaceCard = readFileSync(new URL('../src/space-card.ts', import.meta.url), 'utf8');
const spaceRender = readFileSync(new URL('../src/space-render.ts', import.meta.url), 'utf8');
@@ -15,12 +17,16 @@ test('Labs iso is presentation-only and absent from the secondary space card', (
assert.doesNotMatch(spaceRender, /hp-labs|iso-walls|projection-toggle|view\.volumetric/);
});
test('renderer uses SVG geometry without a CSS 3D context or new window light', () => {
test('Stage 2 uses inert shared-projection SVG geometry without a second light model', () => {
assert.match(card, /<svg class="iso-underlay-svg"/);
assert.match(card, /<svg class="iso-shadows-svg"/);
assert.match(card, /<svg class="iso-walls-svg"/);
assert.match(card, /class="iso-wall-top"/);
assert.match(card, /class="iso-wall-side"/);
assert.match(card, /class="iso-opening-panel iso-\$\{panel\.type\}"/);
assert.match(card, /aria-hidden="true" pointer-events="none"/);
assert.doesNotMatch(styles, /perspective\s*:|preserve-3d|rotateX\(|rotateZ\(/);
assert.doesNotMatch(card, /iso-window|window-light|vertical-door/);
assert.doesNotMatch(card, /window-light|iso-window-light|iso-glow|iso-sun/);
});
test('all current floor/live layers remain in the one main scene', () => {
@@ -31,3 +37,19 @@ test('all current floor/live layers remain in the one main scene', () => {
assert.match(card, /const point = this\._scenePoint\(\[pos\.x, pos\.y\]\)/);
assert.match(card, /const point = this\._scenePoint\(\[p\.x, p\.y\]\)/);
});
test('structural cache includes opening flips and excludes live HA amount', () => {
assert.match(card, /flipH: !!opening\.flip_h/);
assert.match(card, /flipV: !!opening\.flip_v/);
assert.match(card, /floorEdgeHeight: ISO_FLOOR_EDGE_HEIGHT, algorithm: 3/);
const source = card.slice(card.indexOf('private _isoSource()'), card.indexOf('private _isoSceneKey()'));
assert.doesNotMatch(source, /_openingAmt|openingAmount|\.hass|matchMedia|CSS\.supports|theme|hover/);
assert.match(card, /projectIsoOpening\(basis, this\._openingAmt\(opening\)\)/);
assert.match(card, /isoLayers && !isoLayers\.floorSymbols/);
});
test('Stage 2 adds no schema, dependency, storage, network or HA action surface', () => {
assert.doesNotMatch(openings, /localStorage|fetch\(|XMLHttpRequest|WebSocket|callService|config|schema/i);
assert.doesNotMatch(packageJson, /three|babylon|webgl/i);
assert.doesNotMatch(card, /iso2|isometric_stage|stage2_enabled/);
});