Files
houseplan-card/demo/golden/matrix.mjs
T

1276 lines
74 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { fixtureWallKey } from '../fixtures/visual-matrix.mjs';
/** Data-only HP-QA-01 capture matrix. Bump when framing or scenarios change. */
export const GOLDEN_MATRIX_VERSION = 66;
const stage = { capture: 'stage', threshold: { maxChannelDelta: 10, maxDiffRatio: 0.0005 } };
const page = { capture: 'page', threshold: { maxChannelDelta: 10, maxDiffRatio: 0.0008 } };
const sunWindow = { capture: 'sun-window', threshold: { maxChannelDelta: 10, maxDiffRatio: 0.001 } };
const openingRoomContract = {
kind: 'room', wallCm: 30, surface: 'flat',
openings: [
{ id: 'golden-centred-door', type: 'door', at: 0.25, length: 0.10,
flipV: false, offset: 'center' },
{ id: 'golden-centred-window', type: 'window', at: 0.50, length: 0.10,
flipV: false, offset: 'center' },
{ id: 'golden-centred-gate', type: 'gate', at: 0.75, length: 0.10,
flipV: false, offset: 'center' },
],
};
const openingPartitionContract = {
kind: 'partition', wallCm: 30, surface: 'flat',
a: [0.14, 0.22], b: [0.86, 0.78],
openings: [
{ id: 'golden-diagonal-door', type: 'door', at: 0.22, length: 0.09,
flipV: false, offset: 'center' },
{ id: 'golden-diagonal-window', type: 'window', at: 0.50, length: 0.09,
flipV: false, offset: 'center' },
{ id: 'golden-diagonal-gate', type: 'gate', at: 0.78, length: 0.09,
flipV: false, offset: 'center' },
],
};
const openingFlipContract = {
kind: 'room', wallCm: 30, surface: 'flat',
openings: [
{ id: 'golden-door-default', type: 'door', at: 0.16, length: 0.08,
flipV: false, offset: 'center' },
{ id: 'golden-door-flipped', type: 'door', at: 0.30, length: 0.08,
flipV: true, offset: 'center' },
{ id: 'golden-window-default', type: 'window', at: 0.44, length: 0.08,
flipV: false, offset: 'center' },
{ id: 'golden-window-flipped', type: 'window', at: 0.58, length: 0.08,
flipV: true, offset: 'center' },
{ id: 'golden-gate-default', type: 'gate', at: 0.72, length: 0.08,
flipV: false, offset: 'center', turnPair: 'golden-gate-turn' },
{ id: 'golden-gate-flipped', type: 'gate', at: 0.86, length: 0.08,
flipV: true, offset: 'center', turnPair: 'golden-gate-turn' },
],
};
const openingIsoContract = {
kind: 'room', wallCm: 30, surface: 'iso',
openings: [
{ id: 'golden-iso-door', type: 'door', at: 0.20, length: 0.09,
flipV: false, offset: 'selected-face' },
{ id: 'golden-iso-window', type: 'window', at: 0.40, length: 0.09,
flipV: false, offset: 'center' },
{ id: 'golden-iso-gate-default', type: 'gate', at: 0.62, length: 0.09,
flipV: false, offset: 'selected-face', turnPair: 'golden-iso-gate-turn' },
{ id: 'golden-iso-gate-flipped', type: 'gate', at: 0.82, length: 0.09,
flipV: true, offset: 'selected-face', turnPair: 'golden-iso-gate-turn' },
],
};
const stage3DenseMarkers = [
{ id: 'golden-light-one', binding: 'device:golden-light-one', value_badge: {
enabled: true,
source: { kind: 'entity_state', entity_id: 'light.golden_light_one' },
position: 'right',
} },
{ id: 'golden-light-two', binding: 'device:golden-light-two' },
{ id: 'golden-presence', binding: 'device:golden-presence', display: 'icon_ripple' },
{ id: 'golden-climate', binding: 'device:golden-climate', value_badge: {
enabled: true,
source: {
kind: 'entity_attribute', entity_id: 'climate.golden_climate',
attribute: 'current_temperature',
},
position: 'top',
} },
{ id: 'golden-left-linkquality', binding: 'device:golden-left-linkquality', value_badge: {
enabled: true,
source: { kind: 'entity_state', entity_id: 'sensor.golden_left_linkquality' },
position: 'bottom',
} },
{ id: 'golden-right-linkquality', binding: 'device:golden-right-linkquality' },
];
const stage3DenseLayout = {
'golden-light-one': { s: 'golden-lighting', x: 0.487, y: 0.22 },
'golden-light-two': { s: 'golden-lighting', x: 0.43, y: 0.76 },
'golden-presence': { s: 'golden-lighting', x: 0.513, y: 0.22 },
'golden-climate': { s: 'golden-lighting', x: 0.56, y: 0.64 },
'golden-left-linkquality': { s: 'golden-lighting', x: 0.487, y: 0.43 },
'golden-right-linkquality': { s: 'golden-lighting', x: 0.513, y: 0.76 },
};
const stage3VacuumTrail = {
maxDeviationCm: 17.5,
current: [
[[180, 220], [360, 220], [360, 420], [520, 420]],
[[650, 240], [780, 240], [780, 500], [650, 620], [850, 760]],
],
previous: [[160, 760], [300, 650], [420, 740], [560, 600]],
};
const stage3RequiredOverlays = ['device', 'room-label', 'opening-lock'];
/** Exact existing baseline impact of the opening-symbol change (#242/#250, matrix
* v37): measured by comparing `actualSha256` for HEAD and origin/dev under the
* same Chromium build (baseline status alone was not used — dev already had
* unrelated pending candidates). Every listed frame uses a shared fixture with
* an affected opening or keeps that plan behind an editor/dialog; no other
* existing frame changed. Keep this list reviewable: this is its only copy
* since the testing-notes checklists were removed (#681). */
export const OPENING_SYMBOL_EXISTING_GOLDEN_IMPACT = Object.freeze([
'isometric-geometry-view-dark',
'isometric-geometry-view-light',
'isometric-live-layers-dark',
'isometric-no-borders-dark',
'isometric-touch-kiosk-dark',
'isometric-large-warm-remount-dark',
'geometry-view-dark-fit',
'geometry-view-light-fit',
'room-label-parity-view-dark',
'room-label-parity-plan-dark',
'room-label-parity-view-light',
'room-label-parity-plan-light',
'day-cycle-dawn-dark',
'day-cycle-day-dark',
'day-cycle-dusk-dark',
'day-cycle-night-dark',
'geometry-plan-editor-dark',
'space-tab-drop-before-light',
'space-tab-drop-after-dark',
'plan-snap-endpoint-light',
'plan-snap-line-gaps-dark',
'junction-patch-resilience-plan-dark',
'opening-placement-door-thick-wall-dark',
'opening-placement-passage-thick-wall-dark',
'opening-placement-passage-thick-wall-light',
'geometry-devices-editor-dark',
'geometry-decor-editor-dark',
'tray-wide-selection-en',
'tray-wide-tool-ru',
'tray-medium-group-en',
'tray-medium-selection-ru',
'tray-narrow-palette-en',
'tray-narrow-tool-ru',
'geometry-diagonal-45-opening-dark',
'openings-thick-wall-dark',
'lighting-glow-sun-dark',
'device-value-badge-positions-dark',
'device-icon-state-table-light',
'device-icon-state-table-dark',
'device-text-shell-long-light',
'device-text-shell-long-dark',
'lighting-sun-window-state-only-dark',
'lighting-fill-light-axis-split-dark',
'lighting-fill-temp-axis-split-dark',
'lighting-fill-lqi-axis-split-dark',
'lighting-temp-glow-dark',
'lighting-temp-glow-light',
'lighting-custom-glow-dark',
'lighting-opaque-glow-two-doorways-dark',
'lighting-custom-glow-light',
'lighting-temp-glow-no-sources-dark',
'lighting-temp-glow-room-override-dark',
'lighting-manual-auto-spill-overlap-dark',
'hover-over-glow-dark',
'hover-nested-room-dark',
'large-house-zoom-040-dark',
'large-house-zoom-250-dark',
'large-house-warm-remount-dark',
'device-dialog-desktop-en',
'device-help-popover-light-ru',
'decor-color-popover-desktop-en',
'general-color-popover-desktop-en',
'space-room-color-popover-desktop-ru',
'backup-full-preview-desktop-en',
'backup-plan-only-export-desktop-en',
'optimize-preflight-dialog-dark-en',
'optimize-preflight-dialog-light-ru',
]);
const decorLayerFixture = [
{ id: 'golden-decor-axis', kind: 'line', x1: 0.10, y1: 0.54, x2: 0.90, y2: 0.54,
color: '#ff0033', opacity: 1, width_cm: 8 },
{ id: 'golden-decor-rect', kind: 'rect', x: 0.15, y: 0.28, w: 0.16, h: 0.12,
color: '#ff0033', opacity: 1, width_cm: 2, fill: true,
fill_color: '#ff6680', fill_opacity: 1 },
{ id: 'golden-decor-ellipse', kind: 'ellipse', x: 0.69, y: 0.28, w: 0.16, h: 0.12,
color: '#ff0033', opacity: 1, width_cm: 2, fill: true,
fill_color: '#ff6680', fill_opacity: 1 },
{ id: 'golden-decor-text', kind: 'text', x: 0.24, y: 0.72, text: 'DECOR',
color: '#ff0033', opacity: 1, size_cm: 14 },
{ id: 'golden-decor-sofa', kind: 'furniture', symbol: 'sofa',
x: 0.68, y: 0.66, w: 0.18, h: 0.13, color: '#ff0033', opacity: 1, width_cm: 2 },
];
const decorLayerProbes = {
color: '#ff0033', radius: 2, minMatchingFraction: 0.6,
points: [
{ id: 'left-room', x: 0.25, y: 0.54 },
{ id: 'opening-tunnel', x: 0.50, y: 0.54 },
{ id: 'right-room', x: 0.75, y: 0.54 },
],
};
const stairLayerFixture = [
{ id: 'golden-stair-straight-large', kind: 'straight', x: 0.25, y: 0.27,
length: 0.28, width: 0.10, angle: 0, direction: 'forward',
target_space_id: 'golden-lighting' },
{ id: 'golden-stair-straight-small', kind: 'straight', x: 0.72, y: 0.27,
length: 0.14, width: 0.065, angle: 45, direction: 'backward',
// #683 AC13: explicit user-owned style stays identical in light/dark themes.
color: '#0066cc', opacity: 0.85, fill_color: '#ffd23f', fill_opacity: 0.55,
target_space_id: 'golden-lighting' },
{ id: 'golden-stair-spiral-large', kind: 'spiral', x: 0.25, y: 0.73,
radius: 0.10, angle: 15, direction: 'clockwise',
target_space_id: 'golden-lighting' },
{ id: 'golden-stair-spiral-small', kind: 'spiral', x: 0.72, y: 0.73,
radius: 0.06, angle: 210, direction: 'counterclockwise',
target_space_id: 'golden-lighting' },
];
/**
* #649/#673 Stage 6 acceptance scenes
* (docs/design/649-25d-stage6/ACCEPTANCE.md): tiles, floor shadows, soft sun
* wash and theme-free walls in the four theme × floor combinations of the
* approved sketch 07 (attachments 09, 10, 12, 13) plus the lifted hover frame
* (attachment 11); sun as the lab default 10:00. These exact objects are part
* of GOLDEN_SCENARIOS below; the separate export keeps the focused diagnostic
* side-by-side capture script useful without defining a second matrix.
*/
export const STAGE6_ACCEPTANCE_SCENARIOS = Object.freeze([
...[['light', 'light'], ['light', 'dark'], ['dark', 'dark'], ['dark', 'light']].map(([theme, floor]) => ({
id: `isometric-stage6-${theme}-${floor}floor`, fixture: 'visual',
space: 'golden-lighting', mode: 'view', projection: 'iso',
stage3Fixture: { roomMetrics: true, lockOpening: 'light-door', lockState: 'locked' },
stage3Golden: { requiredKinds: stage3RequiredOverlays },
markerOverrides: stage3DenseMarkers,
layoutOverrides: stage3DenseLayout,
stateOverrides: {
'light.golden_light_one': { attributes: { lqi: 40 } },
'light.golden_light_two': { attributes: { lqi: 120 } },
'sensor.golden_right_linkquality': { state: '210' },
'sun.sun': { attributes: { azimuth: 150, elevation: 52 } },
},
fillMode: 'custom', customFill: { c: floor === 'light' ? '#eee8de' : '#737777', a: 1 },
glowEnabled: false, sunRays: true, northDeg: 180,
// The lab's walls are the default white; the colour is the user's either way.
wallFill: { c: '#ffffff', a: 1 },
theme, viewport: { width: 1000, height: 900 }, ...stage,
})),
{ id: 'isometric-stage6-hover-light', fixture: 'visual',
space: 'golden-lighting', mode: 'view', projection: 'iso',
stage3Fixture: { roomMetrics: true, lockOpening: 'light-door', lockState: 'locked' },
stage3Golden: { requiredKinds: stage3RequiredOverlays },
markerOverrides: stage3DenseMarkers,
layoutOverrides: stage3DenseLayout,
fillMode: 'custom', customFill: { c: '#eee8de', a: 1 },
glowEnabled: false, sunRays: false,
hoverDevice: 'golden-light-two',
theme: 'light', viewport: { width: 1000, height: 900 }, ...stage,
},
]);
export const GOLDEN_SCENARIOS = Object.freeze([
...STAGE6_ACCEPTANCE_SCENARIOS,
// #663 AC3/AC10/AC12: both stair geometries, both rise directions and
// small/large footprints across themes, flat/2.5D and interaction states.
{ id: 'stairs-flat-normal-light', fixture: 'visual', space: 'golden-geometry', mode: 'view',
stairsOverride: stairLayerFixture, language: 'en', theme: 'light',
viewport: { width: 1000, height: 900 }, ...stage },
{ id: 'stairs-flat-hover-dark', fixture: 'visual', space: 'golden-geometry', mode: 'view',
stairsOverride: stairLayerFixture, hoverStair: 'golden-stair-straight-large',
language: 'ru', theme: 'dark', viewport: { width: 1000, height: 900 }, ...stage },
{ id: 'stairs-flat-selected-light', fixture: 'visual', space: 'golden-geometry', mode: 'plan',
stairsOverride: stairLayerFixture, stairSelection: 'golden-stair-spiral-large',
language: 'en', theme: 'light', viewport: { width: 1000, height: 900 }, ...stage },
{ id: 'stairs-isometric-dark', fixture: 'visual', space: 'golden-geometry', mode: 'view',
projection: 'iso', stairsOverride: stairLayerFixture,
language: 'en', theme: 'dark', viewport: { width: 1000, height: 900 }, ...stage },
// #486 AC7: the custom HA panel owns the full viewport. These three focused
// frames pin its populated View, its Plan-editor chrome and the deliberately
// inert read-only first-run state without duplicating the card matrix below.
{ id: 'panel-wide-view-light-en', fixture: 'visual', space: 'golden-geometry',
panelHost: true, mode: 'view', language: 'en', theme: 'light',
viewport: { width: 1280, height: 800 }, ...page },
{ id: 'panel-wide-plan-editor-dark-en', fixture: 'visual', space: 'golden-geometry',
panelHost: true, mode: 'plan', language: 'en', theme: 'dark',
viewport: { width: 1280, height: 800 }, ...page },
{ id: 'panel-narrow-read-only-empty-light-en', fixture: 'visual',
panelHost: true, narrow: true, emptyPlan: true, canWrite: false, userIsAdmin: false,
preloadEditorRuntime: false, mode: 'view', language: 'en', theme: 'light',
viewport: { width: 320, height: 720 }, ...page },
// #462 AC12: the card-level version recovery notice must stay compact,
// readable and outside layout/fit flow on desktop, narrow touch and the
// manual-only kiosk state after this tab already attempted the target.
{ id: 'version-mismatch-desktop-dark-en', fixture: 'visual', space: 'golden-geometry',
mode: 'view', integrationVersion: '0.0.0-golden-backend',
language: 'en', theme: 'dark', viewport: { width: 1000, height: 900 }, ...page },
{ id: 'version-mismatch-touch-light-ru', fixture: 'visual', space: 'golden-geometry',
mode: 'view', integrationVersion: '0.0.0-golden-backend', touchViewport: true,
language: 'ru', theme: 'light', viewport: { width: 390, height: 760 }, ...page },
{ id: 'version-mismatch-kiosk-attempted-dark-de', fixture: 'visual', space: 'golden-geometry',
mode: 'view', integrationVersion: '0.0.0-golden-backend',
versionRecoveryAttempted: true, kiosk: true,
language: 'de', theme: 'dark', viewport: { width: 820, height: 760 }, ...page },
{ id: 'hidden-wall-diagnostics-plan-light', fixture: 'visual',
space: 'golden-coincident-partition', coincidentPartition: 'before',
hiddenWallDiagnostics: true, mode: 'plan', theme: 'light',
viewport: { width: 1000, height: 900 }, ...stage },
{ id: 'hidden-wall-diagnostics-plan-dark', fixture: 'visual',
space: 'golden-coincident-partition', coincidentPartition: 'before',
hiddenWallDiagnostics: true, mode: 'plan', theme: 'dark',
viewport: { width: 1000, height: 900 }, ...stage },
{ id: 'coincident-partition-before-dark', fixture: 'visual',
space: 'golden-coincident-partition', coincidentPartition: 'before',
mode: 'view', theme: 'dark', viewport: { width: 1000, height: 900 }, ...stage },
{ id: 'coincident-partition-thin-dark', fixture: 'visual',
space: 'golden-coincident-partition', coincidentPartition: 'thin',
mode: 'view', theme: 'dark', viewport: { width: 1000, height: 900 }, ...stage },
{ id: 'coincident-partition-thick-dark', fixture: 'visual',
space: 'golden-coincident-partition', coincidentPartition: 'thick',
mode: 'view', theme: 'dark', viewport: { width: 1000, height: 900 }, ...stage },
{ id: 'coincident-partition-virtual-dark', fixture: 'visual',
space: 'golden-coincident-partition', coincidentPartition: 'virtual',
mode: 'view', theme: 'dark', viewport: { width: 1000, height: 900 }, ...stage },
// #316 §3.1: a legacy span over a wall with a door — after the migration the
// door keeps its carrying atom while the border is zero on both sides.
{ id: 'span-over-door-migrated-dark', fixture: 'visual',
space: 'golden-span-over-door', spanOverDoor: true,
mode: 'view', theme: 'dark', viewport: { width: 1000, height: 900 }, ...stage },
{ id: 'split-corner-wall-before-dark', fixture: 'visual', space: 'golden-corner-split',
cornerSplitWall: 'before', mode: 'view', theme: 'dark', viewport: { width: 1000, height: 900 }, ...stage },
{ id: 'split-corner-wall-thin-dark', fixture: 'visual', space: 'golden-corner-split',
cornerSplitWall: 'thin', mode: 'view', theme: 'dark', viewport: { width: 1000, height: 900 }, ...stage },
{ id: 'split-corner-wall-thick-dark', fixture: 'visual', space: 'golden-corner-split',
cornerSplitWall: 'thick', mode: 'view', theme: 'dark', viewport: { width: 1000, height: 900 }, ...stage },
{ id: 'split-zero-divider-taper-dark', fixture: 'visual', space: 'golden-zero-divider',
cornerSplitWall: 'zero-taper', mode: 'view', theme: 'dark', viewport: { width: 1000, height: 900 }, ...stage },
{ id: 'isometric-geometry-view-dark', fixture: 'visual', space: 'golden-geometry', mode: 'view',
// Stage 2 material/floor-edge plus door, window, gate and nested-room coverage.
projection: 'iso',
theme: 'dark', viewport: { width: 1000, height: 900 }, ...stage },
{ id: 'isometric-geometry-view-light', fixture: 'visual', space: 'golden-geometry', mode: 'view',
projection: 'iso',
theme: 'light', viewport: { width: 1000, height: 900 }, ...stage },
{ id: 'isometric-live-layers-dark', fixture: 'visual', space: 'golden-lighting', mode: 'view',
projection: 'iso',
theme: 'dark', viewport: { width: 1000, height: 900 }, ...stage },
{ id: 'isometric-no-borders-dark', fixture: 'visual', space: 'golden-lighting', mode: 'view',
projection: 'iso', showBorders: false, fillMode: 'custom',
customFill: { c: '#486a8f', a: 0.42 }, glowEnabled: true,
stage3Golden: { noBorders: true },
theme: 'dark', viewport: { width: 1000, height: 900 }, ...stage },
{ id: 'isometric-touch-kiosk-dark', fixture: 'visual', space: 'golden-geometry', mode: 'view',
projection: 'iso', kiosk: true,
theme: 'dark', viewport: { width: 390, height: 760 }, ...stage },
{ id: 'isometric-large-warm-remount-dark', fixture: 'large', space: 'perf-floor-2', mode: 'view',
projection: 'iso', warmRemount: true,
theme: 'dark', viewport: { width: 1180, height: 900 }, ...stage },
...['light', 'dark'].map((theme) => ({
id: `isometric-stage3-overlays-${theme}`, fixture: 'visual',
space: 'golden-lighting', mode: 'view', projection: 'iso',
stage3Fixture: {
roomMetrics: true, lockOpening: 'light-door', lockState: 'locked',
newDevice: 'golden-presence',
},
stage3Golden: {
requiredKinds: stage3RequiredOverlays, requireNudged: true,
requireMaterialDefs: true, maxMaterialDefs: 12,
requireVacuumFloor: theme === 'dark',
requireDenseFacets: true,
requireNoTetherCues: true,
requireNoGrounding: true,
requireLiveLayers: theme === 'dark',
},
markerOverrides: stage3DenseMarkers,
markerAreaSnapshot: {
'golden-light-one': { binding: 'device:golden-light-one', area: 'golden_light_left' },
'golden-light-two': { binding: 'device:golden-light-two', area: 'golden_light_left' },
'golden-presence': { binding: 'device:golden-presence', area: 'golden_light_right' },
'golden-climate': { binding: 'device:golden-climate', area: 'golden_light_right' },
'golden-left-linkquality': {
binding: 'device:golden-left-linkquality', area: 'golden_light_left',
},
'golden-right-linkquality': {
binding: 'device:golden-right-linkquality', area: 'golden_light_right',
},
},
stateOverrides: {
'light.golden_light_one': { attributes: { lqi: 40 } },
'light.golden_light_two': { attributes: { lqi: 120 } },
'sensor.golden_right_linkquality': { state: '210' },
...(theme === 'dark' ? {
'sun.sun': { attributes: { azimuth: 270, elevation: 24 } },
} : {}),
},
layoutOverrides: stage3DenseLayout,
...(theme === 'dark' ? {
vacuumTrail: stage3VacuumTrail,
fillMode: 'custom', customFill: { c: '#486a8f', a: 0.42 },
hoverRoom: 'light-left', decorOverride: decorLayerFixture,
glowEnabled: true, sunRays: true, northDeg: 90,
} : {}),
theme, viewport: { width: 1000, height: 900 }, ...stage,
})),
{ id: 'isometric-stage3-openings-dark', fixture: 'visual',
space: 'golden-lighting', mode: 'view', projection: 'iso',
showNames: false, glowEnabled: false, sunRays: false,
stage3Fixture: { lockOpening: 'light-door', lockState: 'unlocked' },
extraOpenings: [{
id: 'stage3-passage', type: 'passage', x: 0.50, y: 0.30,
angle: 90, length: 0.12,
}],
stage3Golden: {
requiredKinds: ['device', 'opening-lock'],
requireMaterialDefs: true, maxMaterialDefs: 12,
openingKinds: ['door', 'window', 'gate'], requirePassage: true,
},
theme: 'dark', viewport: { width: 1000, height: 900 }, ...stage },
{ id: 'isometric-stage3-forced-colors-dark', fixture: 'visual',
space: 'golden-lighting', mode: 'view', projection: 'iso',
forcedColors: true,
stage3Fixture: { roomMetrics: true, lockOpening: 'light-door', lockState: 'locked' },
stage3Golden: {
requiredKinds: stage3RequiredOverlays, requireNudged: true,
requireNoMaterialDefs: true, requireNoTetherCues: true, requireNoGrounding: true,
},
layoutOverrides: stage3DenseLayout,
theme: 'dark', viewport: { width: 1000, height: 900 }, ...stage },
{ id: 'isometric-stage3-no-filter-dark', fixture: 'visual',
space: 'golden-lighting', mode: 'view', projection: 'iso',
disableIsoFilters: true,
stage3Fixture: { roomMetrics: true, lockOpening: 'light-door', lockState: 'locked' },
stage3Golden: {
requiredKinds: stage3RequiredOverlays, requireNudged: true,
requireNoMaterialDefs: true, requireNoTetherCues: true, requireNoGrounding: true,
},
layoutOverrides: stage3DenseLayout,
theme: 'dark', viewport: { width: 1000, height: 900 }, ...stage },
{ id: 'geometry-view-dark-fit', fixture: 'visual', space: 'golden-geometry', mode: 'view',
theme: 'dark', viewport: { width: 1000, height: 900 }, ...stage },
{ id: 'geometry-view-light-fit', fixture: 'visual', space: 'golden-geometry', mode: 'view',
theme: 'light', viewport: { width: 1000, height: 900 }, ...stage },
{ id: 'pdf-export-geometry-light', fixture: 'visual', space: 'golden-geometry', mode: 'view',
pdfExport: true, theme: 'light', viewport: { width: 1000, height: 900 }, ...page },
{ id: 'pdf-export-polish-light', fixture: 'visual', space: 'golden-geometry', mode: 'view',
pdfExport: true, northDeg: 90,
// One almost-zero seam exercises duplicate collapse in the complete PDF
// pipeline; the zero-thickness bottom edge keeps that material contract in
// the reviewed frame without growing a second fixture family.
wallReplacements: [
{
match: { a: [0.06, 0.08], b: [0.48, 0.08] },
segments: [
{ key: fixtureWallKey([0.06, 0.08], [0.479999, 0.08]),
a: [0.06, 0.08], b: [0.479999, 0.08], cm: 10 },
{ key: fixtureWallKey([0.479999, 0.08], [0.48, 0.08]),
a: [0.479999, 0.08], b: [0.48, 0.08], cm: 10 },
],
},
{
match: { a: [0.06, 0.92], b: [0.48, 0.92] },
segments: [{ key: fixtureWallKey([0.06, 0.92], [0.48, 0.92]),
a: [0.06, 0.92], b: [0.48, 0.92], cm: 0 }],
},
],
pdfSemantic: {
minSceneInkPixels: 2_000,
minSceneCoverage: 0.20,
maxCenterOffsetMm: 3,
minSceneClearanceMm: 0.1,
minWallGrayPixels: 600,
maxWallGrayOutsideFieldRatio: 0.02,
minInteriorHatchPixels: 30,
opening: {
xFraction: (0.26 - 0.06) / (0.94 - 0.06),
controlOffsetFraction: 0.16,
halfWidthFraction: 0.025,
maxGrayRatio: 0.65,
minWhiteFraction: 0.40,
},
minCompassInkPixels: 60,
maxDimensionAxisErrorDeg: 0.5,
minRepeatedDimensionLabels: 1,
forbiddenText: ['wall', 'partition', 'door', 'window', 'gate'],
},
theme: 'light', viewport: { width: 1000, height: 900 }, ...page },
{ id: 'pdf-export-stepped-dimensions-light', fixture: 'visual',
space: 'golden-pdf-stepped', mode: 'view', pdfExport: true, pdfSteppedExterior: true,
pdfSemantic: {
minSceneInkPixels: 1_000,
minSceneCoverage: 0.15,
maxCenterOffsetMm: 3,
minSceneClearanceMm: 0.1,
minWallGrayPixels: 400,
maxWallGrayOutsideFieldRatio: 0.02,
minInteriorHatchPixels: 20,
minCompassInkPixels: 60,
maxDimensionAxisErrorDeg: 0.5,
minRepeatedDimensionLabels: 0,
requiredDimensionLabels: ['1.20 m', '2.40 m', '3.60 m', '3.75 m', '7.35 m', '9.75 m'],
forbiddenText: ['wall', 'partition', 'door', 'window', 'gate'],
},
theme: 'light', viewport: { width: 1000, height: 900 }, ...page },
{ id: 'vacuum-trail-smoothing-dark', fixture: 'visual', space: 'golden-geometry', mode: 'view',
vacuumTrail: {
maxDeviationCm: 17.5,
current: [
[[180, 220], [360, 220], [360, 420], [520, 420]],
[[650, 240], [780, 240], [780, 500], [650, 620], [850, 760]],
],
previous: [[160, 760], [300, 650], [420, 740], [560, 600]],
},
theme: 'dark', viewport: { width: 1000, height: 900 }, ...stage },
{ id: 'room-label-parity-view-dark', fixture: 'visual', space: 'golden-lighting', mode: 'view',
roomLabelParity: true, theme: 'dark', viewport: { width: 1000, height: 900 }, ...page },
{ id: 'room-label-parity-plan-dark', fixture: 'visual', space: 'golden-lighting', mode: 'plan',
roomLabelParity: true, theme: 'dark', viewport: { width: 1000, height: 900 }, ...page },
{ id: 'room-label-parity-view-light', fixture: 'visual', space: 'golden-lighting', mode: 'view',
roomLabelParity: true, theme: 'light', viewport: { width: 1000, height: 900 }, ...page },
{ id: 'room-label-parity-plan-light', fixture: 'visual', space: 'golden-lighting', mode: 'plan',
roomLabelParity: true, theme: 'light', viewport: { width: 1000, height: 900 }, ...page },
{ id: 'washer-active-cycle-dark', fixture: 'visual', applianceLifecycle: true,
space: 'golden-appliance', mode: 'view',
stateOverrides: { 'sensor.golden_washer_status': { state: 'start' } },
theme: 'dark', viewport: { width: 1000, height: 900 }, ...stage },
{ id: 'washer-idle-cycle-dark', fixture: 'visual', applianceLifecycle: true,
space: 'golden-appliance', mode: 'view',
stateOverrides: { 'sensor.golden_washer_status': { state: 'done' } },
theme: 'dark', viewport: { width: 1000, height: 900 }, ...stage },
{ id: 'day-cycle-dawn-dark', fixture: 'visual', space: 'golden-geometry', mode: 'view',
bgMode: 'daynight', dayCycle: { phase: 'dawn', top: '#aabdd1' },
stateOverrides: { 'sun.sun': { attributes: { azimuth: 95, elevation: -2, rising: true } } },
theme: 'dark', viewport: { width: 1000, height: 900 }, ...stage },
{ id: 'day-cycle-day-dark', fixture: 'visual', space: 'golden-geometry', mode: 'view',
bgMode: 'daynight', dayCycle: { phase: 'day', top: '#dce9ef' },
stateOverrides: { 'sun.sun': { attributes: { azimuth: 180, elevation: 42, rising: false } } },
theme: 'dark', viewport: { width: 1000, height: 900 }, ...stage },
{ id: 'day-cycle-dusk-dark', fixture: 'visual', space: 'golden-geometry', mode: 'view',
bgMode: 'daynight', dayCycle: { phase: 'dusk', top: '#48536c' },
stateOverrides: { 'sun.sun': { attributes: { azimuth: 265, elevation: -2, rising: false } } },
theme: 'dark', viewport: { width: 1000, height: 900 }, ...stage },
{ id: 'day-cycle-night-dark', fixture: 'visual', space: 'golden-geometry', mode: 'view',
bgMode: 'daynight', dayCycle: { phase: 'night', top: '#111a27' },
stateOverrides: { 'sun.sun': { attributes: { azimuth: 0, elevation: -12, rising: false } } },
theme: 'dark', viewport: { width: 1000, height: 900 }, ...stage },
{ id: 'geometry-plan-editor-dark', fixture: 'visual', space: 'golden-geometry', mode: 'plan',
theme: 'dark', viewport: { width: 1180, height: 900 }, ...page },
{ id: 'safe-resize-handles-clamp-light', fixture: 'visual', space: 'golden-safe-resize',
safeResizeFixture: true, safeResizePreview: true, iconSize: 0.8, mode: 'plan',
theme: 'light', viewport: { width: 1180, height: 900 }, ...page },
{ id: 'safe-resize-handles-clamp-dark', fixture: 'visual', space: 'golden-safe-resize',
safeResizeFixture: true, safeResizePreview: true, iconSize: 0.8, mode: 'plan',
theme: 'dark', viewport: { width: 1180, height: 900 }, ...page },
{ id: 'space-tab-drop-before-light', fixture: 'visual', applianceLifecycle: true,
space: 'golden-geometry', mode: 'plan', tabDrag: 'before',
theme: 'light', viewport: { width: 1180, height: 900 }, ...page },
{ id: 'space-tab-drop-after-dark', fixture: 'visual', applianceLifecycle: true,
space: 'golden-geometry', mode: 'plan', tabDrag: 'after',
theme: 'dark', viewport: { width: 1180, height: 900 }, ...page },
{ id: 'plan-snap-endpoint-light', fixture: 'visual', space: 'golden-geometry', mode: 'plan',
planSnap: { tool: 'draw', pointer: [0.48, 0.48], expectedKind: 'endpoint' },
theme: 'light', viewport: { width: 1180, height: 900 }, ...page },
{ id: 'plan-snap-line-gaps-dark', fixture: 'visual', space: 'golden-geometry', mode: 'plan',
planSnap: {
tool: 'draw', anchor: [0.70, 0.455], pointer: [0.482, 0.455], expectedKind: 'line',
},
theme: 'dark', viewport: { width: 1180, height: 900 }, ...page },
{ id: 'wall-junctions-plan-preview-light', fixture: 'visual',
space: 'golden-wall-junctions', wallJunctions: true, mode: 'plan',
wallJunctionPreview: {
path: [[0.16, 0.15], [0.38, 0.15]], pointer: [0.38, 0.25], cms: [12], cm: 24,
},
theme: 'light', viewport: { width: 1180, height: 900 }, ...page },
{ id: 'wall-junctions-plan-t-dark', fixture: 'visual',
space: 'golden-wall-junctions', wallJunctions: true, mode: 'plan',
wallJunctionPreview: {
path: [[0.50, 0.54]], pointer: [0.50, 0.70], cms: [], cm: 24,
},
theme: 'dark', viewport: { width: 1180, height: 900 }, ...page },
{ id: 'wall-junctions-view-dark', fixture: 'visual',
space: 'golden-wall-junctions', wallJunctions: true, mode: 'view',
theme: 'dark', viewport: { width: 1000, height: 900 }, ...stage },
{ id: 'junction-patch-resilience-plan-dark', fixture: 'visual',
space: 'golden-junction-patch-resilience', junctionPatchResilience: true, mode: 'plan',
retainedWedgeProbe: [0.8955, 0.556],
absentWallProbes: [[0.420833333, 0.37625], [0.92, 0.348]],
theme: 'dark', viewport: { width: 1180, height: 900 }, ...page },
{ id: 'junction-patch-resilience-view-dark', fixture: 'visual',
space: 'golden-junction-patch-resilience', junctionPatchResilience: true, mode: 'view',
retainedWedgeProbe: [0.8955, 0.556],
absentWallProbes: [[0.420833333, 0.37625], [0.92, 0.348]],
theme: 'dark', viewport: { width: 1000, height: 900 }, ...stage },
{ id: 'wall-union-isolation-view-light', fixture: 'visual',
space: 'golden-wall-union-isolation', wallUnionIsolation: true, mode: 'view',
theme: 'light', viewport: { width: 1000, height: 900 }, ...stage },
{ id: 'wall-union-isolation-view-dark', fixture: 'visual',
space: 'golden-wall-union-isolation', wallUnionIsolation: true, mode: 'view',
theme: 'dark', viewport: { width: 1000, height: 900 }, ...stage },
{ id: 'multiwall-junction-bevel-view-dark', fixture: 'visual',
space: 'golden-multiwall-junction', mode: 'view',
multiWallJunction: {
node: [0.329166667, 0.141666667], rays: 3,
// #302: the chamfer is strip-safe — this point sits in the overlap of
// two incident strips and must stay FILLED (it used to be discarded by
// the old subtractive bevel, which is exactly the hole class fixed).
retainedOverlapProbe: [0.3303808442725, 0.1488560107825],
// Product contract (#272): a bounded exterior bevel is allowed, but no
// empty component may remain trapped inside the continuous masonry.
enclosedHoles: 0,
},
theme: 'dark', viewport: { width: 1000, height: 900 }, ...stage },
{ id: 'orthogonal-strip-cell-5-view-dark', fixture: 'visual',
space: 'golden-orthogonal-strip-cell-5', mode: 'view',
orthogonalStripContainment: { caseId: 'cell-5-mixed-depth-t', minSamples: 300 },
theme: 'dark', viewport: { width: 1000, height: 900 }, ...stage },
{ id: 'orthogonal-strip-cell-1-view-dark', fixture: 'visual',
space: 'golden-orthogonal-strip-cell-1', mode: 'view',
orthogonalStripContainment: { caseId: 'cell-1-thick-crossbar-t', minSamples: 300 },
theme: 'dark', viewport: { width: 1000, height: 900 }, ...stage },
{ id: 'wall-key-roundtrip-view-dark', fixture: 'visual',
space: 'golden-wall-key-roundtrip', mode: 'view',
wallKeyRoundtrip: {
variant: 'affected',
node: [0.8875, 0.345833333],
incidentArm: [0.8875, 0.333833333],
},
theme: 'dark', viewport: { width: 1000, height: 900 }, ...stage },
{ id: 'isometric-wall-junctions-dark', fixture: 'visual',
space: 'golden-wall-junctions', wallJunctions: true, mode: 'view',
projection: 'iso',
theme: 'dark', viewport: { width: 1000, height: 900 }, ...stage },
{ id: 'opening-placement-door-thick-wall-dark', fixture: 'visual', space: 'golden-geometry',
// The shared centre edge is a long 25 cm physical wall. It can contain the
// complete 90 cm door preset while still proving rotation, centreline
// placement and ruler geometry on a thick wall.
mode: 'plan', openingPreview: { type: 'door', pointer: [0.48, 0.65] },
openingPreviewPixels: { minPixels: 150, minInsideWallPixels: 8, minChannelDelta: 4 },
theme: 'dark', viewport: { width: 1180, height: 900 }, ...page },
{ id: 'opening-placement-passage-thick-wall-dark', fixture: 'visual', space: 'golden-geometry',
mode: 'plan', openingPreview: { type: 'passage', pointer: [0.48, 0.65] },
openingPreviewPixels: { minPixels: 150, minInsideWallPixels: 8, minChannelDelta: 4 },
theme: 'dark', viewport: { width: 1180, height: 900 }, ...page },
{ id: 'opening-placement-passage-thick-wall-light', fixture: 'visual', space: 'golden-geometry',
mode: 'plan', openingPreview: { type: 'passage', pointer: [0.48, 0.65] },
openingPreviewPixels: { minPixels: 150, minInsideWallPixels: 8, minChannelDelta: 4 },
theme: 'light', viewport: { width: 1180, height: 900 }, ...page },
{ id: 'opening-symbol-room-wall-light', fixture: 'visual', space: 'golden-opening-symbols',
mode: 'view', openingSymbolContract: openingRoomContract,
theme: 'light', viewport: { width: 1000, height: 900 }, ...stage },
{ id: 'opening-symbol-diagonal-partition-dark', fixture: 'visual', space: 'golden-opening-symbols',
mode: 'view', openingSymbolContract: openingPartitionContract,
theme: 'dark', viewport: { width: 1000, height: 900 }, ...stage },
{ id: 'opening-symbol-flip-pairs-light', fixture: 'visual', space: 'golden-opening-symbols',
mode: 'view', openingSymbolContract: openingFlipContract,
theme: 'light', viewport: { width: 1000, height: 900 }, ...stage },
{ id: 'isometric-opening-symbol-parity-dark', fixture: 'visual', space: 'golden-opening-symbols',
mode: 'view', projection: 'iso',
openingSymbolContract: openingIsoContract,
theme: 'dark', viewport: { width: 1000, height: 900 }, ...stage },
{ id: 'geometry-devices-editor-dark', fixture: 'visual', space: 'golden-geometry', mode: 'devices',
theme: 'dark', viewport: { width: 1180, height: 900 }, ...page },
{ id: 'device-inbox-desktop-en-light', fixture: 'visual', space: 'golden-lighting',
dialog: 'device-inbox', language: 'en', theme: 'light',
viewport: { width: 1180, height: 900 }, ...page },
{ id: 'device-inbox-desktop-ru-dark', fixture: 'visual', space: 'golden-lighting',
dialog: 'device-inbox', language: 'ru', theme: 'dark',
viewport: { width: 1180, height: 900 }, ...page },
{ id: 'device-inbox-narrow-ru-dark', fixture: 'visual', space: 'golden-lighting',
dialog: 'device-inbox', deviceInboxTab: 'hidden',
markerOverrides: [{ id: 'golden-light-two', binding: 'device:golden-light-two', hidden: true }],
language: 'ru', theme: 'dark', viewport: { width: 390, height: 900 }, ...page },
{ id: 'geometry-decor-editor-dark', fixture: 'visual', space: 'golden-geometry', mode: 'decor',
theme: 'dark', viewport: { width: 1180, height: 900 }, ...page },
// HP-UX-11 visual contract: every adaptive width is captured in both
// languages while the six materially different tray contents are open.
{ id: 'tray-wide-selection-en', fixture: 'visual', space: 'golden-geometry', mode: 'plan',
editorTray: 'plan-selection', language: 'en', theme: 'dark',
viewport: { width: 1180, height: 900 }, ...page },
{ id: 'tray-wide-tool-ru', fixture: 'visual', space: 'golden-geometry', mode: 'plan',
editorTray: 'plan-tool', language: 'ru', theme: 'dark',
viewport: { width: 1180, height: 900 }, ...page },
{ id: 'tray-medium-group-en', fixture: 'visual', space: 'golden-geometry', mode: 'plan',
editorTray: 'group', language: 'en', theme: 'dark',
viewport: { width: 760, height: 820 }, ...page },
{ id: 'tray-medium-selection-ru', fixture: 'visual', space: 'golden-geometry', mode: 'decor',
editorTray: 'decor-selection', language: 'ru', theme: 'dark',
viewport: { width: 760, height: 820 }, ...page },
{ id: 'tray-narrow-palette-en', fixture: 'visual', space: 'golden-geometry', mode: 'decor',
editorTray: 'furniture-palette', furniturePalette: 'categories', language: 'en', theme: 'dark',
viewport: { width: 390, height: 760 }, ...page },
{ id: 'furniture-categories-light', fixture: 'visual', space: 'golden-geometry', mode: 'decor',
furniturePalette: 'categories', furnitureCategoryArt: 'dishwasher',
language: 'ru', theme: 'light',
// #640: keep the appliance row, including Dishwasher, fully visible so a
// menu-art replacement cannot pass behind the palette footer or CI reuse.
viewport: { width: 900, height: 1050 }, ...page },
{ id: 'furniture-variants-dark', fixture: 'visual', space: 'golden-geometry', mode: 'decor',
furniturePalette: 'variants', furnitureCategory: 'sofa', language: 'en', theme: 'dark',
viewport: { width: 900, height: 820 }, ...page },
{ id: 'furniture-variants-light', fixture: 'visual', space: 'golden-geometry', mode: 'decor',
furniturePalette: 'variants', furnitureCategory: 'coffee_table', language: 'ru', theme: 'light',
viewport: { width: 900, height: 820 }, ...page },
{ id: 'furniture-placement-preview-light', fixture: 'visual', space: 'golden-geometry', mode: 'decor',
furniturePlacementPreview: { symbol: 'sofa', widthCm: 180, depthCm: 90, pointer: [0.35, 0.90] },
language: 'ru', theme: 'light', viewport: { width: 1000, height: 900 }, ...stage },
{ id: 'furniture-plan-art-dark', fixture: 'visual', space: 'golden-geometry', mode: 'view',
decorOverride: [
...decorLayerFixture.filter((shape) => shape.kind !== 'furniture'),
{ id: 'furn-replaced', kind: 'furniture', symbol: 'sofa', x: 0.16, y: 0.18,
w: 0.18, h: 0.09, color: '#141832', opacity: 1, width_cm: 1.2 },
{ id: 'furn-retained', kind: 'furniture', symbol: 'fridge', x: 0.43, y: 0.18,
w: 0.06, h: 0.065, color: '#141832', opacity: 1, width_cm: 1.2 },
{ id: 'furn-new', kind: 'furniture', symbol: 'sofa_corner_right', x: 0.58, y: 0.18,
w: 0.26, h: 0.17, color: '#141832', opacity: 1, width_cm: 1.2 },
],
language: 'en', theme: 'dark', viewport: { width: 1000, height: 900 }, ...stage },
// #593 AC7: четыре ID, которых до пакета 0.4.0 не существовало. Кадр — их
// единственное доказательство «видно на плане»: остальные мебельные сцены
// сняты на символах, которые были и раньше.
{ id: 'furniture-new-symbols-light', fixture: 'visual', space: 'golden-geometry', mode: 'view',
decorOverride: [
...decorLayerFixture.filter((shape) => shape.kind !== 'furniture'),
{ id: 'furn-computer', kind: 'furniture', symbol: 'computer', x: 0.14, y: 0.18,
w: 0.12, h: 0.06, color: '#141832', opacity: 1, width_cm: 1.2 },
{ id: 'furn-hood', kind: 'furniture', symbol: 'hood', x: 0.32, y: 0.18,
w: 0.06, h: 0.05, color: '#141832', opacity: 1, width_cm: 1.2 },
{ id: 'furn-oven', kind: 'furniture', symbol: 'oven', x: 0.44, y: 0.18,
w: 0.06, h: 0.06, color: '#141832', opacity: 1, width_cm: 1.2 },
{ id: 'furn-cactus', kind: 'furniture', symbol: 'cactus', x: 0.58, y: 0.18,
w: 0.07, h: 0.12, color: '#141832', opacity: 1, width_cm: 1.2 },
],
language: 'ru', theme: 'light', viewport: { width: 1000, height: 900 }, ...stage },
// #606: the old saved id and the new public id draw the same machine; the
// bookshelf/shelf_floor pair must be visibly different in their fixed boxes.
{ id: 'furniture-corrected-art-light', fixture: 'visual', space: 'golden-geometry', mode: 'view',
decorOverride: [
{ id: 'furn-legacy-cactus', kind: 'furniture', symbol: 'cactus', x: 0.10, y: 0.18,
w: 0.07, h: 0.12, angle: 25, flip_h: true,
color: '#141832', opacity: 1, width_cm: 1.2 },
{ id: 'furn-exercise', kind: 'furniture', symbol: 'exercise', x: 0.28, y: 0.18,
w: 0.07, h: 0.12, color: '#141832', opacity: 1, width_cm: 1.2 },
{ id: 'furn-bookshelf', kind: 'furniture', symbol: 'bookshelf', x: 0.14, y: 0.54,
w: 0.19, h: 0.07, angle: 20, flip_v: true,
color: '#141832', opacity: 1, width_cm: 1.2 },
{ id: 'furn-shelf-floor', kind: 'furniture', symbol: 'shelf_floor', x: 0.14, y: 0.80,
w: 0.19, h: 0.07, color: '#141832', opacity: 1, width_cm: 1.2 },
],
language: 'ru', theme: 'light', viewport: { width: 1000, height: 900 }, ...stage },
{ id: 'furniture-transform-light', fixture: 'visual', space: 'golden-geometry', mode: 'decor',
decorOverride: [
...decorLayerFixture.filter((shape) => shape.kind !== 'furniture'),
{ id: 'furn-normal', kind: 'furniture', symbol: 'sofa_corner_right', x: 0.08, y: 0.16,
w: 0.18, h: 0.12, color: '#141832', opacity: 1, width_cm: 1.2 },
{ id: 'furn-flip-h', kind: 'furniture', symbol: 'sofa_corner_right', x: 0.30, y: 0.16,
w: 0.18, h: 0.12, flip_h: true, color: '#141832', opacity: 1, width_cm: 1.2 },
{ id: 'furn-flip-v', kind: 'furniture', symbol: 'sofa_corner_right', x: 0.52, y: 0.16,
w: 0.18, h: 0.12, flip_v: true, color: '#141832', opacity: 1, width_cm: 1.2 },
{ id: 'furn-flip-hv', kind: 'furniture', symbol: 'sofa_corner_right', x: 0.74, y: 0.16,
w: 0.18, h: 0.12, flip_h: true, flip_v: true,
color: '#141832', opacity: 1, width_cm: 1.2 },
],
decorSelection: 'furn-flip-hv', language: 'ru', theme: 'light',
viewport: { width: 1000, height: 900 }, ...stage },
{ id: 'furniture-transform-dark', fixture: 'visual', space: 'golden-geometry', mode: 'view',
decorOverride: [
...decorLayerFixture.filter((shape) => shape.kind !== 'furniture'),
{ id: 'furn-normal', kind: 'furniture', symbol: 'sofa_corner_right', x: 0.08, y: 0.16,
w: 0.18, h: 0.12, angle: 30, color: '#d8def0', opacity: 1, width_cm: 1.2 },
{ id: 'furn-flip-h', kind: 'furniture', symbol: 'sofa_corner_right', x: 0.30, y: 0.16,
w: 0.18, h: 0.12, angle: 30, flip_h: true,
color: '#d8def0', opacity: 1, width_cm: 1.2 },
{ id: 'furn-flip-v', kind: 'furniture', symbol: 'sofa_corner_right', x: 0.52, y: 0.16,
w: 0.18, h: 0.12, angle: 30, flip_v: true,
color: '#d8def0', opacity: 1, width_cm: 1.2 },
{ id: 'furn-flip-hv', kind: 'furniture', symbol: 'sofa_corner_right', x: 0.74, y: 0.16,
w: 0.18, h: 0.12, angle: 30, flip_h: true, flip_v: true,
color: '#d8def0', opacity: 1, width_cm: 1.2 },
],
language: 'en', theme: 'dark', viewport: { width: 1000, height: 900 }, ...stage },
{ id: 'tray-narrow-tool-ru', fixture: 'visual', space: 'golden-geometry', mode: 'decor',
editorTray: 'decor-tool', language: 'ru', theme: 'dark',
viewport: { width: 390, height: 760 }, ...page },
{ id: 'geometry-diagonal-45-opening-dark', fixture: 'visual', space: 'golden-geometry', mode: 'view',
openingGeometry: { id: 'geo-diagonal-window', type: 'window', angle: 45 },
theme: 'dark', viewport: { width: 1000, height: 900 }, ...stage },
{ id: 'openings-thick-wall-dark', fixture: 'visual', space: 'golden-lighting', mode: 'view',
fillMode: 'none', glowEnabled: false, theme: 'dark', viewport: { width: 1000, height: 900 }, ...stage },
{ id: 'openings-filled-tunnel-dark', fixture: 'visual', space: 'golden-lighting', mode: 'view',
fillMode: 'custom', customFill: { c: '#66717c', a: 0.55 }, glowEnabled: false,
hideOpenings: true,
wallReplacements: [{
match: { a: [0.5, 0.1], b: [0.5, 0.88] },
segments: [
{ key: fixtureWallKey([0.5, 0.1], [0.5, 0.5]), a: [0.5, 0.1], b: [0.5, 0.5], cm: 18 },
{ key: fixtureWallKey([0.5, 0.5], [0.5, 0.88]), a: [0.5, 0.5], b: [0.5, 0.88], cm: 32 },
],
}],
tunnelContinuity: { openingId: 'light-door', insetPx: 2, maxChannelJump: 3, dpr2: true },
theme: 'dark', viewport: { width: 1000, height: 900 }, ...stage },
{ id: 'decor-over-opaque-hover-light', fixture: 'visual', space: 'golden-lighting', mode: 'view',
fillMode: 'custom', customFill: { c: '#2255cc', a: 1 }, glowEnabled: false,
sunRays: false, showBorders: false, showNames: false, hideOpenings: true,
hoverRoom: 'light-left', decorOverride: decorLayerFixture,
decorPixelProbes: decorLayerProbes,
theme: 'light', viewport: { width: 1000, height: 900 }, ...stage },
{ id: 'decor-over-glow-base-dark', fixture: 'visual', space: 'golden-lighting', mode: 'view',
fillMode: 'glow', glowEnabled: true, allLightsOff: true,
sunRays: false, showBorders: false, showNames: false, hideOpenings: true,
decorOverride: decorLayerFixture, decorPixelProbes: decorLayerProbes,
theme: 'dark', viewport: { width: 1000, height: 900 }, ...stage },
{ id: 'openings-hidden-view-dark', fixture: 'visual', space: 'golden-lighting', mode: 'view',
fillMode: 'none', glowEnabled: false, hideOpenings: true, theme: 'dark', viewport: { width: 1000, height: 900 }, ...stage },
{ id: 'lighting-glow-sun-dark', fixture: 'visual', space: 'golden-lighting', mode: 'view',
theme: 'dark', viewport: { width: 1000, height: 900 }, ...stage },
{ id: 'device-value-badge-positions-dark', fixture: 'visual', space: 'golden-lighting', mode: 'view',
glowEnabled: false, sunRays: false,
markerOverrides: [
{ id: 'golden-light-one', binding: 'device:golden-light-one', value_badge: {
enabled: true, source: { kind: 'entity_state', entity_id: 'light.golden_light_one' }, position: 'right',
} },
{ id: 'golden-light-two', binding: 'device:golden-light-two', value_badge: {
enabled: true, source: { kind: 'entity_state', entity_id: 'light.golden_light_two' }, position: 'left',
} },
{ id: 'golden-presence', binding: 'device:golden-presence', value_badge: {
enabled: true, source: { kind: 'entity_state', entity_id: 'binary_sensor.golden_presence' }, position: 'top',
} },
{ id: 'golden-climate', binding: 'device:golden-climate', value_badge: {
enabled: true,
source: { kind: 'entity_attribute', entity_id: 'climate.golden_climate', attribute: 'current_temperature' },
position: 'bottom',
} },
],
stateOverrides: { 'climate.golden_climate': { attributes: { lqi: 190 } } },
layoutOverrides: {
'golden-light-one': { s: 'golden-lighting', x: 0.20, y: 0.32 },
'golden-light-two': { s: 'golden-lighting', x: 0.20, y: 0.72 },
'golden-presence': { s: 'golden-lighting', x: 0.80, y: 0.68 },
'golden-climate': { s: 'golden-lighting', x: 0.80, y: 0.28 },
},
theme: 'dark', viewport: { width: 1000, height: 900 }, ...stage },
{ id: 'device-value-face-cover-source-dark', fixture: 'visual',
space: 'golden-lighting', mode: 'view', glowEnabled: false, sunRays: false,
showNames: false,
markerOverrides: [{
id: 'golden-left-linkquality', binding: 'entity:cover.golden_door_half',
display: 'value', size: 3,
value_source: {
kind: 'entity_attribute', entity_id: 'cover.golden_door_half',
attribute: 'current_position',
},
}],
stateOverrides: {
'cover.golden_door_half': { attributes: { current_position: 42 } },
},
layoutOverrides: {
'golden-left-linkquality': { s: 'golden-lighting', x: 0.50, y: 0.50 },
},
deviceOnly: 'golden-left-linkquality',
theme: 'dark', viewport: { width: 1000, height: 900 }, ...stage },
...['light', 'dark'].map((theme) => ({
id: `device-icon-state-table-${theme}`, fixture: 'visual', space: 'golden-lighting', mode: 'view',
glowEnabled: false, sunRays: false,
markerOverrides: [
// #588: пятый режим показан там же, где живут остальные четыре. Источник
// выбран цветной (включённая RGB-лампа) намеренно: на нейтральном кадр
// совпал бы с прежним, и эталон не доказывал бы ничего.
{ id: 'golden-light-one', binding: 'device:golden-light-one',
display: 'value_static_icon' },
{ id: 'golden-light-two', binding: 'device:golden-light-two' },
{ id: 'golden-light-three', binding: 'device:golden-light-three' },
{ id: 'golden-presence', binding: 'device:golden-presence' },
{ id: 'golden-climate', binding: 'device:golden-climate' },
{ id: 'golden-left-temperature', binding: 'device:golden-left-temperature', display: 'value' },
{ id: 'golden-right-temperature', binding: 'device:golden-right-temperature', display: 'static_icon' },
{ id: 'golden-left-linkquality', binding: 'device:golden-left-linkquality',
tap_action: 'toggle', controls: ['light.golden_light_three'], value_badge: {
enabled: true,
source: { kind: 'entity_state', entity_id: 'sensor.golden_left_linkquality' },
position: 'right',
} },
{ id: 'golden-right-linkquality', binding: 'device:golden-right-linkquality', value_badge: {
enabled: true,
source: { kind: 'entity_state', entity_id: 'sensor.golden_right_linkquality' },
position: 'bottom',
} },
],
// This scenario deliberately places two devices outside their HA rooms to
// exercise icon states at fixed coordinates. Seed the Area-following
// provenance so #126 does not reinterpret that test-only layout as stale
// legacy placement and turn an unrelated visual matrix into a relocation
// scenario.
markerAreaSnapshot: {
'golden-light-one': { binding: 'device:golden-light-one', area: 'golden_light_left' },
'golden-light-two': { binding: 'device:golden-light-two', area: 'golden_light_left' },
'golden-light-three': { binding: 'device:golden-light-three', area: 'golden_light_right' },
'golden-presence': { binding: 'device:golden-presence', area: 'golden_light_right' },
'golden-climate': { binding: 'device:golden-climate', area: 'golden_light_right' },
'golden-left-temperature': {
binding: 'device:golden-left-temperature', area: 'golden_light_left',
},
'golden-right-temperature': {
binding: 'device:golden-right-temperature', area: 'golden_light_right',
},
'golden-left-linkquality': {
binding: 'device:golden-left-linkquality', area: 'golden_light_left',
},
'golden-right-linkquality': {
binding: 'device:golden-right-linkquality', area: 'golden_light_right',
},
},
stateOverrides: {
'light.golden_light_one': { attributes: { lqi: 40 } },
'light.golden_light_two': { attributes: { lqi: 41 } },
'light.golden_light_three': { state: 'unavailable', attributes: { lqi: 180 } },
'sensor.golden_left_temperature': { state: 'Complete long localized state' },
'sensor.golden_right_temperature': { state: 'unavailable' },
},
layoutOverrides: {
'golden-light-one': { s: 'golden-lighting', x: 0.16, y: 0.25 },
'golden-light-two': { s: 'golden-lighting', x: 0.34, y: 0.25 },
'golden-light-three': { s: 'golden-lighting', x: 0.66, y: 0.25 },
'golden-presence': { s: 'golden-lighting', x: 0.84, y: 0.25 },
'golden-climate': { s: 'golden-lighting', x: 0.16, y: 0.72 },
'golden-left-temperature': { s: 'golden-lighting', x: 0.34, y: 0.72 },
'golden-right-temperature': { s: 'golden-lighting', x: 0.66, y: 0.72 },
'golden-left-linkquality': { s: 'golden-lighting', x: 0.77, y: 0.55 },
'golden-right-linkquality': { s: 'golden-lighting', x: 0.84, y: 0.72 },
},
deviceClassOverrides: {
'golden-light-two': ['lock-locked'],
'golden-light-three': ['lock-unlocked'],
'golden-presence': ['virtual'],
'golden-climate': ['alarm', 'sel'],
'golden-right-linkquality': ['open'],
},
focusDevice: 'golden-climate',
hoverDevice: 'golden-presence',
hideHoverTooltip: true,
theme, viewport: { width: 1000, height: 900 }, ...stage,
})),
...['light', 'dark'].map((theme) => ({
id: `device-text-shell-long-${theme}`, fixture: 'visual', space: 'golden-lighting', mode: 'view',
glowEnabled: false, sunRays: false, showNames: false,
markerOverrides: [{
id: 'golden-left-linkquality', binding: 'device:golden-left-linkquality',
display: 'value', size: 3,
}],
stateOverrides: {
'sensor.golden_left_linkquality': {
state: '498', attributes: { unit_of_measurement: 'ppm' },
},
},
layoutOverrides: {
'golden-left-linkquality': { s: 'golden-lighting', x: 0.50, y: 0.50 },
},
deviceOnly: 'golden-left-linkquality',
theme, viewport: { width: 1000, height: 900 }, ...stage,
})),
{ id: 'lighting-sun-window-state-only-dark', fixture: 'visual', space: 'golden-lighting', mode: 'view',
// The golden screenshot is backed by a second, sun-layer-hidden capture.
// A real painted ray must account for enough changed pixels; DOM-only
// presence or an accidentally accepted empty baseline is not sufficient.
glowEnabled: false, allLightsOff: true, northDeg: 90,
stateOverrides: { 'sun.sun': { attributes: { azimuth: 270, elevation: 24 } } },
sunRayPixels: { minPixels: 500, minChannelDelta: 4 },
theme: 'dark', viewport: { width: 1000, height: 900 }, ...sunWindow },
{ id: 'lighting-sun-window-outer-thick-dark', fixture: 'visual', space: 'golden-lighting', mode: 'view',
// The matching inner scene above is not enough for #577: this frame pins
// the exterior source edge, the physical 15 cm tunnel and its seam with
// the clean room floor on a real rendered wall.
glowEnabled: false, allLightsOff: true, northDeg: 90, sunRayOrigin: 'outer',
stateOverrides: { 'sun.sun': { attributes: { azimuth: 270, elevation: 24 } } },
sunRayPixels: { minPixels: 500, minChannelDelta: 4 },
theme: 'dark', viewport: { width: 1000, height: 900 }, ...sunWindow },
...[1, 2].map((deviceScaleFactor) => ({
id: `lighting-sun-window-outer-occluded-dpr${deviceScaleFactor}-dark`,
fixture: 'visual', space: 'golden-lighting', mode: 'view',
// #580: diagonal light through the real 15 cm tunnel must narrow at the
// inner jamb instead of continuing through solid wall. Pin both raster
// scales because the tunnel/room seam is exactly where subpixels differ.
glowEnabled: false, allLightsOff: true, northDeg: 90, sunRayOrigin: 'outer',
stateOverrides: { 'sun.sun': { attributes: { azimuth: 240, elevation: 24 } } },
sunRayPixels: { minPixels: 400, minChannelDelta: 4 }, deviceScaleFactor,
theme: 'dark', viewport: { width: 1000, height: 900 }, ...sunWindow,
})),
{ id: 'lighting-fill-light-axis-split-dark', fixture: 'visual', space: 'golden-lighting', mode: 'view',
fillMode: 'light', glowEnabled: false, theme: 'dark', viewport: { width: 1000, height: 900 }, ...stage },
{ id: 'lighting-fill-temp-axis-split-dark', fixture: 'visual', space: 'golden-lighting', mode: 'view',
fillMode: 'temp', glowEnabled: false, theme: 'dark', viewport: { width: 1000, height: 900 }, ...stage },
{ id: 'lighting-fill-lqi-axis-split-dark', fixture: 'visual', space: 'golden-lighting', mode: 'view',
fillMode: 'lqi', glowEnabled: false, theme: 'dark', viewport: { width: 1000, height: 900 }, ...stage },
{ id: 'lighting-temp-glow-dark', fixture: 'visual', space: 'golden-lighting', mode: 'view',
fillMode: 'temp', glowEnabled: true, theme: 'dark', viewport: { width: 1000, height: 900 }, ...stage },
{ id: 'lighting-temp-glow-light', fixture: 'visual', space: 'golden-lighting', mode: 'view',
fillMode: 'temp', glowEnabled: true, theme: 'light', viewport: { width: 1000, height: 900 }, ...stage },
{ id: 'lighting-custom-glow-dark', fixture: 'visual', space: 'golden-lighting', mode: 'view',
fillMode: 'custom', customFill: { c: '#486a8f', a: 0.42 }, glowEnabled: true,
roomCustomFill: { 'light-right': { c: '#8f5f48', a: 0.56 } },
theme: 'dark', viewport: { width: 1000, height: 900 }, ...stage },
{ id: 'lighting-opaque-glow-two-doorways-dark', fixture: 'visual', space: 'golden-lighting', mode: 'view',
fillMode: 'custom', customFill: { c: '#3f4854', a: 1 }, glowEnabled: true, sunRays: false,
allLightsOff: true,
stateOverrides: {
'light.golden_light_one': { state: 'on', attributes: { rgb_color: [255, 196, 112], brightness: 255 } },
},
extraOpenings: [
{
id: 'light-door-second', type: 'door', x: 0.50, y: 0.30,
angle: 90, length: 0.13, contact: 'cover.golden_door_closed',
},
{
id: 'light-door-third', type: 'door', x: 0.50, y: 0.76,
angle: 90, length: 0.13, contact: 'cover.golden_door_half',
},
],
layoutOverrides: { 'golden-light-one': { s: 'golden-lighting', x: 0.40, y: 0.48 } },
// One frame contains the complete #20 matrix: the original unbound door is
// fully open, the upper bound door is closed and the lower positional door
// exposes 50%. Protection is meaningful only if the receiving half also
// contains rendered light; a data-only check once let an empty baseline pass.
warmPixelRegion: { x: 0.5, y: 0, w: 0.5, h: 1, minPixels: 2500, minRedBlueDelta: 25 },
theme: 'dark', viewport: { width: 1000, height: 900 }, ...stage },
{ id: 'lighting-custom-glow-light', fixture: 'visual', space: 'golden-lighting', mode: 'view',
fillMode: 'custom', customFill: { c: '#486a8f', a: 0.42 }, glowEnabled: true,
roomCustomFill: { 'light-right': { c: '#8f5f48', a: 0.56 } },
theme: 'light', viewport: { width: 1000, height: 900 }, ...stage },
{ id: 'lighting-temp-glow-no-sources-dark', fixture: 'visual', space: 'golden-lighting', mode: 'view',
fillMode: 'temp', glowEnabled: true, allLightsOff: true,
theme: 'dark', viewport: { width: 1000, height: 900 }, ...stage },
{ id: 'lighting-temp-glow-room-override-dark', fixture: 'visual', space: 'golden-lighting', mode: 'view',
fillMode: 'temp', glowEnabled: true, roomGlow: { 'light-left': true, 'light-right': false },
theme: 'dark', viewport: { width: 1000, height: 900 }, ...stage },
{ id: 'lighting-manual-auto-spill-overlap-dark', fixture: 'visual', space: 'golden-lighting', mode: 'view',
fillMode: 'temp', glowEnabled: true,
markerOverrides: [{
id: 'golden-light-two', binding: 'device:golden-light-two',
glow_color: { c: '#3a8fff', bri: 0.35 }, glow_radius_cm: 420,
}],
layoutOverrides: { 'golden-light-two': { s: 'golden-lighting', x: 0.39, y: 0.57 } },
theme: 'dark', viewport: { width: 1000, height: 900 }, ...stage },
{ id: 'hover-over-glow-dark', fixture: 'visual', space: 'golden-lighting', mode: 'view',
hoverRoom: 'light-right', theme: 'dark', viewport: { width: 1000, height: 900 }, ...stage },
{ id: 'hover-nested-room-dark', fixture: 'visual', space: 'golden-geometry', mode: 'view',
hoverRoom: 'geo-nested', theme: 'dark', viewport: { width: 1000, height: 900 }, ...stage },
// --- #302: junction close-ups. One node per scene, the node fills the
// frame; the same scenes carry the objective no-holes detector in tests.
...[
['junction-t-90-equal15-dark', { arms: [
{ deg: 0, cm: 15 }, { deg: 90, cm: 15 }, { deg: 180, cm: 15 }] }],
['junction-t-90-bar50-leg15-dark', { arms: [
{ deg: 0, cm: 50 }, { deg: 90, cm: 15 }, { deg: 180, cm: 50 }] }],
['junction-t-90-bar70-leg15-dark', { arms: [
{ deg: 0, cm: 70 }, { deg: 90, cm: 15 }, { deg: 180, cm: 70 }] }],
['junction-x-90-equal15-dark', { arms: [
{ deg: 0, cm: 15 }, { deg: 90, cm: 15 }, { deg: 180, cm: 15 },
{ deg: 270, cm: 15 }] }],
['junction-x-90-mixed-dark', { arms: [
{ deg: 0, cm: 15 }, { deg: 90, cm: 50 }, { deg: 180, cm: 15 },
{ deg: 270, cm: 70 }] }],
['junction-x-45-alternating-dark', { arms: [
{ deg: 45, cm: 15 }, { deg: 135, cm: 50 }, { deg: 225, cm: 15 },
{ deg: 315, cm: 50 }] }],
['junction-star5-equal15-dark', { arms: [
{ deg: 0, cm: 15 }, { deg: 72, cm: 15 }, { deg: 144, cm: 15 },
{ deg: 216, cm: 15 }, { deg: 288, cm: 15 }] }],
['junction-y-60-equal50-dark', { arms: [
{ deg: 0, cm: 50 }, { deg: 60, cm: 50 }, { deg: 120, cm: 50 }] }],
['junction-acute30-mixed-dark', { arms: [
{ deg: 0, cm: 50 }, { deg: 30, cm: 70 }, { deg: 180, cm: 15 }] }],
['junction-acute15-equal50-dark', { arms: [
{ deg: 0, cm: 50 }, { deg: 15, cm: 50 }, { deg: 180, cm: 50 }] }],
['junction-splay10-170-dark', { arms: [
{ deg: 0, cm: 15 }, { deg: 10, cm: 15 }, { deg: 180, cm: 50 }] }],
['junction-t-virtual-arm-dark', { arms: [
{ deg: 0, cm: 15 }, { deg: 90, cm: 15, virtual: true },
{ deg: 180, cm: 15 }] }],
['junction-x-virtual-through-dark', { arms: [
{ deg: 0, cm: 15 }, { deg: 90, cm: 15, virtual: true },
{ deg: 180, cm: 15 }, { deg: 270, cm: 15, virtual: true }] }],
['junction-column-node-dark', { column: true, arms: [
{ deg: 0, cm: 15 }, { deg: 90, cm: 15 }, { deg: 180, cm: 15 }] }],
['junction-draft-end-node-dark', { draft: true, arms: [
{ deg: 0, cm: 15 }, { deg: 90, cm: 15 }, { deg: 180, cm: 15 }] }],
].map(([id, junctionNode]) => ({
id, fixture: 'visual', space: `golden-${id}`, mode: 'view', junctionNode,
zoom: 4, theme: 'dark', viewport: { width: 900, height: 900 }, ...stage,
})),
...[
['junction-309-step-dark', 8, [808.33, 1233.33]],
['junction-309-spike-dark', 8, [2220.83, 1350]],
['junction-309-hump-dark', 6, [1758.33, 1612.5]],
].map(([id, zoom, zoomCenter]) => ({
id, fixture: 'visual', space: `golden-${id}`, mode: 'view', junctionTeeth: true,
zoom, zoomCenter, theme: 'dark', viewport: { width: 900, height: 900 }, ...stage,
})),
{ id: 'sharp-apex-legacy-dark', fixture: 'visual',
space: 'golden-sharp-apex', mode: 'view', sharpApex: true,
theme: 'dark',
viewport: { width: 900, height: 900 }, ...stage },
{ id: 'junction-owner-repro-dark', fixture: 'visual',
space: 'golden-junction-owner-repro', mode: 'view', junctionArtifacts: true,
zoom: 6, zoomCenter: [329.17, 141.67], theme: 'dark',
viewport: { width: 900, height: 900 }, ...stage },
{ id: 'large-house-zoom-040-dark', fixture: 'large', space: 'perf-floor-1', mode: 'view',
zoom: 0.4, theme: 'dark', viewport: { width: 1180, height: 900 }, ...stage },
{ id: 'large-house-zoom-250-dark', fixture: 'large', space: 'perf-floor-1', mode: 'view',
zoom: 2.5, theme: 'dark', viewport: { width: 1180, height: 900 }, ...stage },
{ id: 'large-house-warm-remount-dark', fixture: 'large', space: 'perf-floor-2', mode: 'view',
warmRemount: true, theme: 'dark', viewport: { width: 1180, height: 900 }, ...stage },
{ id: 'device-dialog-desktop-en', fixture: 'visual', space: 'golden-lighting',
dialog: 'device', deviceId: 'golden-light-two', deviceLightControls: true,
deviceName: 'Living room light controller with an intentionally long title',
openHelp: 'marker.glow_mode.help', helpTextRegion: { key: 'marker.glow_mode.help', minPixels: 30 },
focusDialogClose: true, language: 'en', theme: 'dark', viewport: { width: 1180, height: 1200 }, ...page },
{ id: 'device-dialog-mobile-ru', fixture: 'visual', space: 'golden-lighting',
dialog: 'device', deviceId: 'golden-light-two', deviceLightControls: true,
deviceName: 'Контроллер освещения гостиной с намеренно очень длинным названием',
language: 'ru', theme: 'dark', viewport: { width: 390, height: 1000 }, ...page },
{ id: 'device-dialog-desktop-de', fixture: 'visual', space: 'golden-lighting',
dialog: 'device', deviceId: 'golden-light-two', deviceLightControls: true,
deviceName: 'Wohnzimmer-Lichtsteuerung mit absichtlich sehr langem Namen',
language: 'de', theme: 'light', viewport: { width: 1180, height: 1200 }, ...page },
{ id: 'room-temperature-dialog-desktop-en', fixture: 'visual', space: 'golden-lighting',
dialog: 'room-temperature', roomTemperature: { min: 18, max: 22 },
language: 'en', theme: 'light', viewport: { width: 900, height: 900 }, ...page },
{ id: 'room-temperature-dialog-mobile-ru', fixture: 'visual', space: 'golden-lighting',
dialog: 'room-temperature', roomTemperature: { min: 18, max: null },
language: 'ru', theme: 'dark', viewport: { width: 390, height: 820 }, ...page },
{ id: 'room-discard-dialog-mobile-ru', fixture: 'visual', space: 'golden-lighting',
dialog: 'room-discard', language: 'ru', theme: 'light',
viewport: { width: 320, height: 820 }, ...page },
{ id: 'german-view-mobile-light', fixture: 'visual', space: 'golden-geometry',
mode: 'view', language: 'de', theme: 'light',
viewport: { width: 390, height: 900 }, ...page },
{ id: 'toggle-entity-dialog-desktop-en', fixture: 'visual', applianceLifecycle: true,
space: 'golden-appliance', dialog: 'device', deviceId: 'golden-washer',
deviceToggleEntity: 'selected', markerOverrides: [{
id: 'golden-washer', binding: 'device:golden-washer', tap_action: 'toggle',
toggle_entity: 'switch.golden_washer_child_lock',
}],
language: 'en', theme: 'light', viewport: { width: 1180, height: 1100 }, ...page },
{ id: 'toggle-entity-dialog-mobile-ru', fixture: 'visual', applianceLifecycle: true,
space: 'golden-appliance', dialog: 'device', deviceId: 'golden-washer',
deviceToggleEntity: 'stale', markerOverrides: [{
id: 'golden-washer', binding: 'device:golden-washer', tap_action: 'toggle',
toggle_entity: 'switch.golden_washer_removed',
}],
language: 'ru', theme: 'dark', viewport: { width: 390, height: 1000 }, ...page },
{ id: 'device-help-popover-light-ru', fixture: 'visual', space: 'golden-lighting',
dialog: 'device', deviceId: 'golden-light-two', deviceLightControls: true,
openHelp: 'marker.glow_mode.help', helpTextRegion: { key: 'marker.glow_mode.help', minPixels: 30 },
language: 'ru', theme: 'light', viewport: { width: 760, height: 900 }, ...page },
{ id: 'decor-color-popover-mobile-ru', fixture: 'visual', space: 'golden-geometry',
dialog: 'decor-color', language: 'ru', theme: 'dark',
viewport: { width: 390, height: 760 }, ...page },
{ id: 'decor-color-popover-desktop-en', fixture: 'visual', space: 'golden-geometry',
dialog: 'decor-color', language: 'en', theme: 'light',
viewport: { width: 760, height: 820 }, ...page },
{ id: 'general-color-popover-desktop-en', fixture: 'visual', space: 'golden-geometry',
dialog: 'general-color', language: 'en', theme: 'light',
viewport: { width: 900, height: 900 }, ...page },
// #86 AC7: renderer-level 200% browser zoom is 390 CSS px at DPR 2
// (780 physical px). Keep both themes reviewed because help placement and
// contrast are part of the visual contract, not just a DOM assertion.
{ id: 'settings-help-zoom-200-en-light', fixture: 'visual', space: 'golden-geometry',
dialog: 'general-help', openHelp: 'gs.bg_mode.help', deviceScaleFactor: 2,
helpTextRegion: { key: 'gs.bg_mode.help', minPixels: 30 },
language: 'en', theme: 'light', viewport: { width: 390, height: 900 }, ...page },
{ id: 'settings-help-zoom-200-ru-dark', fixture: 'visual', space: 'golden-geometry',
dialog: 'general-help', openHelp: 'gs.bg_mode.help', deviceScaleFactor: 2,
helpTextRegion: { key: 'gs.bg_mode.help', minPixels: 30 },
language: 'ru', theme: 'dark', viewport: { width: 390, height: 900 }, ...page },
// #43 AC14: the support dialog owns reviewed desktop, phone and tablet
// states. The six scenes deliberately cover both themes while keeping the
// individual states named in the specification independently reviewable.
{ id: 'support-desktop-empty-light-en', fixture: 'visual', space: 'golden-geometry',
dialog: 'support', supportState: 'empty', language: 'en', theme: 'light',
viewport: { width: 1000, height: 980 }, ...page },
{ id: 'support-desktop-preview-dark-en', fixture: 'visual', space: 'golden-geometry',
dialog: 'support', supportState: 'preview', language: 'en', theme: 'dark',
viewport: { width: 1000, height: 980 }, ...page },
{ id: 'support-phone-validation-light-ru', fixture: 'visual', space: 'golden-geometry',
dialog: 'support', supportState: 'validation', language: 'ru', theme: 'light',
viewport: { width: 320, height: 760 }, ...page },
{ id: 'support-phone-success-dark-en', fixture: 'visual', space: 'golden-geometry',
dialog: 'support', supportState: 'success', language: 'en', theme: 'dark',
viewport: { width: 390, height: 820 }, ...page },
{ id: 'support-relay-error-light-en', fixture: 'visual', space: 'golden-geometry',
dialog: 'support', supportState: 'relay-error', language: 'en', theme: 'light',
viewport: { width: 900, height: 900 }, ...page },
{ id: 'support-tablet-preview-dark-ru', fixture: 'visual', space: 'golden-geometry',
dialog: 'support', supportState: 'preview', language: 'ru', theme: 'dark',
viewport: { width: 768, height: 900 }, ...page },
{ id: 'device-ripple-color-popover-mobile-ru', fixture: 'visual', space: 'golden-lighting',
dialog: 'device-ripple-color', deviceId: 'golden-light-two',
language: 'ru', theme: 'dark', viewport: { width: 390, height: 1000 }, ...page },
{ id: 'space-room-color-popover-desktop-ru', fixture: 'visual', space: 'golden-geometry',
dialog: 'space-room-color', language: 'ru', theme: 'dark',
viewport: { width: 900, height: 1000 }, ...page },
{ id: 'backup-full-preview-desktop-en', fixture: 'visual', space: 'golden-geometry',
dialog: 'backup-full', language: 'en', theme: 'dark',
viewport: { width: 1000, height: 900 }, ...page },
{ id: 'backup-plan-only-export-desktop-en', fixture: 'visual', space: 'golden-geometry',
dialog: 'backup-export-plan-only', language: 'en', theme: 'dark',
viewport: { width: 1000, height: 900 }, ...page },
{ id: 'backup-space-preview-mobile-ru', fixture: 'visual', space: 'golden-geometry',
dialog: 'backup-space', language: 'ru', theme: 'light',
viewport: { width: 390, height: 820 }, ...page },
{ id: 'optimize-preflight-dialog-dark-en', fixture: 'visual', space: 'golden-geometry',
dialog: 'optimize-preflight', language: 'en', theme: 'dark',
viewport: { width: 900, height: 760 }, ...page },
{ id: 'optimize-preflight-dialog-light-ru', fixture: 'visual', space: 'golden-geometry',
dialog: 'optimize-preflight', language: 'ru', theme: 'light',
viewport: { width: 900, height: 760 }, ...page },
{ id: 'optimize-orphan-references-dark-en', fixture: 'visual', space: 'golden-geometry',
dialog: 'optimize-orphan-references', language: 'en', theme: 'dark',
markerOverrides: [{
id: 'golden-light-two', binding: 'virtual', space: 'removed-floor',
room_id: 'removed-room', icon: 'mdi:lightbulb', name: 'Living-room lamp',
}, {
id: 'golden-light-one', binding: 'virtual', name: 'Hall lamp',
}, {
id: 'golden-presence', binding: 'virtual', removed: true,
space: 'removed-floor', name: 'Retired marker',
}],
// The orphan-reference fixture intentionally reuses ids of live registry
// devices. Pin their pre-#126 provenance so the lifecycle synchronizer
// cannot consume the deliberately orphaned positions before Optimize
// inspects them.
markerAreaSnapshot: {
'golden-light-one': { binding: 'device:golden-light-one', area: 'golden_light_left' },
'golden-light-two': { binding: 'device:golden-light-two', area: 'golden_light_left' },
'golden-presence': { binding: 'device:golden-presence', area: 'golden_light_right' },
},
layoutOverrides: {
'golden-light-two': { s: 'removed-floor', x: 0.35, y: 0.72 },
'golden-light-one': { s: 'unresolved-floor', x: 0.20, y: 0.34 },
'golden-presence': { s: 'removed-floor', x: 0.45, y: 0.64 },
},
viewport: { width: 900, height: 760 }, ...page },
{ id: 'optimize-orphan-references-light-ru', fixture: 'visual', space: 'golden-geometry',
dialog: 'optimize-orphan-references', language: 'ru', theme: 'light',
markerOverrides: [{
id: 'golden-light-two', binding: 'virtual', space: 'removed-floor',
room_id: 'removed-room', icon: 'mdi:lightbulb', name: 'Лампа в гостиной',
}, {
id: 'golden-light-one', binding: 'virtual', name: 'Лампа в холле',
}, {
id: 'golden-presence', binding: 'virtual', removed: true,
space: 'removed-floor', name: 'Старый маркер',
}],
markerAreaSnapshot: {
'golden-light-one': { binding: 'device:golden-light-one', area: 'golden_light_left' },
'golden-light-two': { binding: 'device:golden-light-two', area: 'golden_light_left' },
'golden-presence': { binding: 'device:golden-presence', area: 'golden_light_right' },
},
layoutOverrides: {
'golden-light-two': { s: 'removed-floor', x: 0.35, y: 0.72 },
'golden-light-one': { s: 'unresolved-floor', x: 0.20, y: 0.34 },
'golden-presence': { s: 'removed-floor', x: 0.45, y: 0.64 },
},
viewport: { width: 900, height: 880 }, ...page },
{ id: 'card-editor-invalid-default-floor-light-en', fixture: 'visual',
cardEditorInvalidDefaultFloor: 'removed-floor', language: 'en', theme: 'light',
viewport: { width: 900, height: 760 }, ...page },
{ id: 'card-editor-invalid-default-floor-dark-ru', fixture: 'visual',
cardEditorInvalidDefaultFloor: 'removed-floor', language: 'ru', theme: 'dark',
viewport: { width: 900, height: 760 }, ...page },
]);