mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-29 03:09:36 +00:00
1276 lines
74 KiB
JavaScript
1276 lines
74 KiB
JavaScript
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 },
|
||
]);
|