mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-29 03:09:36 +00:00
543 lines
23 KiB
JavaScript
543 lines
23 KiB
JavaScript
/**
|
|
* Registry of persisted fields that need an explicit compatibility decision.
|
|
*
|
|
* This is deliberately data-only so the local auditor, documentation tooling
|
|
* and future frontend/backend parity checks can consume the same source. A
|
|
* selector is a JSON path; `*` visits every array item and `equals` narrows a
|
|
* field whose key is still current but whose old value is compatibility-only.
|
|
*/
|
|
export const CONFIG_FIELD_REGISTRY = Object.freeze([
|
|
{
|
|
id: 'settings.summary_panel',
|
|
enforcedBy: 'summary-panel.ts + validation.py change-aware reference validation; #437',
|
|
selector: { path: ['settings', 'summary_panel'] },
|
|
storage: 'House Plan server config',
|
|
type: '{ version: 1, title, show_on_mobile, blocks[] } | bounded future object',
|
|
default: 'unset (= localized read-only defaults; no server write)',
|
|
level: 'global shared presentation',
|
|
ui: 'View → Summary panel settings',
|
|
runtime: 'read-only screen-space overlay; HA entity values remain runtime-only',
|
|
introduced: '#437 / v1.73.0',
|
|
status: 'current',
|
|
migration: 'none — omission derives defaults; explicit empty blocks remain empty',
|
|
compatibility: 'older ordinary config/set preserves the namespace; future versions stay opaque and inert',
|
|
},
|
|
{
|
|
id: 'card.tap_action',
|
|
schema: 'lovelace-card', // #33: Lovelace card option — not in the backend manifest by design
|
|
selector: { path: ['tap_action'] },
|
|
storage: 'Lovelace card config',
|
|
type: "'info' | 'more-info' | 'toggle'",
|
|
default: 'unset',
|
|
level: 'card',
|
|
ui: 'hidden',
|
|
runtime: 'ignored since v1.38.1; marker.tap_action is authoritative',
|
|
introduced: 'legacy',
|
|
status: 'deprecated-read',
|
|
migration: 'remove from Lovelace YAML manually; no visual change',
|
|
compatibility: 'accepted indefinitely until the minimum supported card config is raised',
|
|
},
|
|
{
|
|
id: 'settings.show_all',
|
|
schema: 'allow-extra', // #33: lives only through extra=ALLOW_EXTRA, absent from CONFIG_SCHEMA
|
|
enforcedBy: 'houseplan-card.ts materialisation: seeds hidden markers, then `delete st.show_all`',
|
|
selector: { path: ['settings', 'show_all'] },
|
|
storage: 'House Plan server config',
|
|
type: 'boolean',
|
|
default: 'unset',
|
|
level: 'global',
|
|
ui: 'removed',
|
|
runtime: 'materialisation compatibility only',
|
|
introduced: 'legacy',
|
|
status: 'migrate-on-write',
|
|
migration: 'seed marker visibility, then remove the field',
|
|
compatibility: 'read until all supported configs have passed materialisation once',
|
|
},
|
|
{
|
|
id: 'settings.group_lights',
|
|
schema: 'allow-extra', // #33: lives only through extra=ALLOW_EXTRA, absent from CONFIG_SCHEMA
|
|
selector: { path: ['settings', 'group_lights'] },
|
|
storage: 'House Plan server config',
|
|
type: 'boolean',
|
|
default: 'unset (= true: room lights grouped)',
|
|
level: 'global',
|
|
ui: 'device inbox → Discovery filters (#44)',
|
|
runtime: 'device discovery/materialisation',
|
|
introduced: 'legacy; supported UI since #44',
|
|
status: 'current',
|
|
migration: 'none — a supported setting; true is stored as key absence',
|
|
compatibility: 'unset keeps grouping on, exactly the legacy behaviour',
|
|
enforcedBy: 'demo/smoke_discovery_filters.mjs (#44 AC1)',
|
|
},
|
|
{
|
|
id: 'settings.exclude_integrations',
|
|
schema: 'allow-extra', // #33: lives only through extra=ALLOW_EXTRA, absent from CONFIG_SCHEMA
|
|
selector: { path: ['settings', 'exclude_integrations'] },
|
|
storage: 'House Plan server config',
|
|
type: 'string[]',
|
|
default: 'unset (= the product EXCLUDED_DOMAINS list)',
|
|
level: 'global',
|
|
ui: 'device inbox → Discovery filters (#44)',
|
|
runtime: 'device discovery, materialisation seed, room climate (#44 H2) — one resolver',
|
|
introduced: 'legacy; supported UI since #44',
|
|
status: 'current',
|
|
migration: 'none — a supported setting; a present list REPLACES the product list wholesale',
|
|
compatibility: 'unset keeps the product list; [] is a valid "exclude nothing"',
|
|
enforcedBy: 'effectiveExcludedIntegrations (devices.ts) + demo/smoke_discovery_filters.mjs (#44 AC2)',
|
|
},
|
|
{
|
|
id: 'settings.weather_entity',
|
|
enforcedBy: 'houseplan-editor-runtime.ts _saveSettingsDialog: `delete settings.weather_entity` on every save',
|
|
selector: { path: ['settings', 'weather_entity'] },
|
|
storage: 'House Plan server config',
|
|
type: 'string | null',
|
|
default: 'unset',
|
|
level: 'global',
|
|
ui: 'removed',
|
|
runtime: 'ignored since sun rays became geometry/elevation-only',
|
|
introduced: 'pre-v1.60.2',
|
|
status: 'migrate-on-settings-save',
|
|
migration: 'drop on the next settings save',
|
|
compatibility: 'backend accepts bounded values so old configs still load',
|
|
},
|
|
{
|
|
id: 'settings.marker_area_snapshot',
|
|
enforcedBy: 'device-area-relocation.ts + validation.py; #126',
|
|
selector: { path: ['settings', 'marker_area_snapshot'] },
|
|
storage: 'House Plan server config',
|
|
type: 'Record<markerId, { binding: device:* | entity:*, area: areaId }>',
|
|
default: 'unset (= one-time safe backfill)',
|
|
level: 'global internal lifecycle metadata',
|
|
ui: 'hidden',
|
|
runtime: 'detect authoritative HA Area changes and supersede stale saved positions',
|
|
introduced: '#126 / v1.70.0',
|
|
status: 'current',
|
|
migration: 'safe point-in-room backfill; cross-source full import drops it',
|
|
compatibility: 'older frontends ignore it; same-source backup preserves it',
|
|
},
|
|
{
|
|
id: 'settings.zigbee_topology',
|
|
schema: 'allow-extra',
|
|
enforcedBy: 'zigbee-topology-settings.ts + explicit admin provider controls; #54',
|
|
selector: { path: ['settings', 'zigbee_topology'] },
|
|
storage: 'House Plan server config',
|
|
type: '{ enabled?: boolean, z2m_base_topics?: string[] }',
|
|
default: 'unset (= disabled; default Zigbee2MQTT base topic is presented in UI)',
|
|
level: 'global',
|
|
ui: 'General settings → Zigbee links',
|
|
runtime: 'allows a mouse-only incident-neighbour overlay after explicit provider refresh',
|
|
introduced: '#54',
|
|
status: 'current',
|
|
migration: 'none — topology snapshots remain memory-only',
|
|
compatibility: 'older frontends ignore it; backend preserves it through allow-extra',
|
|
},
|
|
{
|
|
id: 'settings.radar',
|
|
schema: 'allow-extra',
|
|
enforcedBy: 'radar_validation.py + radar coordinator; #485 stage 1',
|
|
selector: { path: ['settings', 'radar'] },
|
|
storage: 'House Plan server config',
|
|
type: '{ version?: 1, show_live?: boolean, ...future stage fields }',
|
|
default: 'unset (= live presence layer enabled)',
|
|
level: 'global',
|
|
ui: 'General settings → Show presence on the plan',
|
|
runtime: 'global display preference; no source subscription or recording consent by itself',
|
|
introduced: '#485 stage 1',
|
|
status: 'current',
|
|
migration: 'none',
|
|
compatibility: 'older frontends ignore it; known writes preserve future siblings',
|
|
},
|
|
{
|
|
id: 'markers[].radar',
|
|
schema: 'allow-extra',
|
|
enforcedBy: 'radar_validation.py + radar coordinator; #485 stage 1',
|
|
selector: { path: ['markers', '*', 'radar'] },
|
|
storage: 'House Plan server config',
|
|
type: 'versioned presence-radar source, installation and calibration config',
|
|
default: 'unset (= marker is not a configured presence radar)',
|
|
level: 'device marker',
|
|
ui: 'Device editor → Presence on plan',
|
|
runtime: 'exact HA source bindings normalized server-side into bounded live frames',
|
|
introduced: '#485 stage 1',
|
|
status: 'current',
|
|
migration: 'none; virtualized imports drop the hardware-specific block',
|
|
compatibility: 'untouched unknown versions remain inert and lossless',
|
|
},
|
|
{
|
|
id: 'markers[].display=ripple',
|
|
enforcedBy: 'logic.ts normalizeDeviceDisplay (read) + editor-runtime _dropLegacySegments rewrites on every write',
|
|
selector: { path: ['markers', '*', 'display'], equals: 'ripple' },
|
|
storage: 'House Plan server config',
|
|
type: "legacy enum value 'ripple'",
|
|
default: 'badge',
|
|
level: 'device marker',
|
|
ui: 'not offered',
|
|
runtime: "normalizeDeviceDisplay maps it to 'icon_ripple'",
|
|
introduced: 'pre-v1.26.0',
|
|
status: 'deprecated-read',
|
|
migration: "write 'icon_ripple' when the marker is edited",
|
|
compatibility: 'backend and frontend keep reading it during the declared compatibility window',
|
|
},
|
|
{
|
|
id: 'markers[].vacuum.trail',
|
|
selector: { path: ['markers', '*', 'vacuum', 'trail'] },
|
|
storage: 'House Plan server config',
|
|
type: 'boolean | null',
|
|
default: 'unset',
|
|
level: 'device marker',
|
|
ui: 'replaced by trail_mode',
|
|
runtime: 'fallback only; trail_mode wins',
|
|
introduced: 'legacy vacuum model',
|
|
status: 'deprecated-read',
|
|
migration: 'convert to trail_mode when vacuum settings are saved or plans are optimized',
|
|
compatibility: 'read while configs without trail_mode remain supported',
|
|
},
|
|
{
|
|
id: 'markers[].vacuum.room_highlight',
|
|
selector: { path: ['markers', '*', 'vacuum', 'room_highlight'] },
|
|
storage: 'House Plan server config',
|
|
type: 'boolean | null',
|
|
default: 'unset',
|
|
level: 'device marker',
|
|
ui: 'hidden',
|
|
runtime: 'no supported consumer found in v1.60.2',
|
|
introduced: 'legacy vacuum model',
|
|
status: 'decision-required',
|
|
migration: 'confirm external use; then document, migrate or remove',
|
|
compatibility: 'preserve until the decision is recorded',
|
|
},
|
|
{
|
|
id: 'markers[].vacuum.segment_map',
|
|
selector: { path: ['markers', '*', 'vacuum', 'segment_map'] },
|
|
storage: 'House Plan server config',
|
|
type: 'Record<string, string>',
|
|
default: 'unset',
|
|
level: 'device marker',
|
|
ui: 'hidden',
|
|
runtime: 'no supported consumer found in v1.60.2',
|
|
introduced: 'legacy vacuum model',
|
|
status: 'decision-required',
|
|
migration: 'confirm external use; then document, migrate or remove',
|
|
compatibility: 'preserve until the decision is recorded',
|
|
},
|
|
{
|
|
id: 'spaces[].aspect',
|
|
enforcedBy: 'validation.py vol.Remove — the backend drops the field during validation',
|
|
selector: { path: ['spaces', '*', 'aspect'] },
|
|
storage: 'House Plan server config',
|
|
type: 'number',
|
|
default: 'unset',
|
|
level: 'space',
|
|
ui: 'removed',
|
|
runtime: 'backend drops it; plan_aspect is canonical',
|
|
introduced: 'legacy backdrop model',
|
|
status: 'drop-on-validation',
|
|
migration: 'removed by backend validation because its coordinate basis is unsafe',
|
|
compatibility: 'stale clients may submit it without failing the whole save',
|
|
},
|
|
{
|
|
id: 'spaces[].segments',
|
|
enforcedBy: 'validation.py vol.Remove — the backend drops the field during validation',
|
|
selector: { path: ['spaces', '*', 'segments'] },
|
|
storage: 'House Plan server config',
|
|
type: 'unknown[]',
|
|
default: 'unset',
|
|
level: 'space',
|
|
ui: 'removed',
|
|
runtime: 'backend drops it; walls derive from room contours',
|
|
introduced: 'pre-v1.19.0',
|
|
status: 'drop-on-validation',
|
|
migration: 'remove on backend validation/write',
|
|
compatibility: 'stale clients may submit it without failing the whole save',
|
|
},
|
|
{
|
|
id: 'spaces[].plan_scale',
|
|
selector: { path: ['spaces', '*', 'plan_scale'] },
|
|
storage: 'House Plan server config',
|
|
type: 'number | null',
|
|
default: '1',
|
|
level: 'space',
|
|
ui: 'replaced by per-axis transform',
|
|
runtime: 'read fallback for plan_scale_x and plan_scale_y',
|
|
introduced: 'pre-v1.58.0',
|
|
status: 'deprecated-read',
|
|
migration: 'materialize equal plan_scale_x/plan_scale_y on explicit backdrop edit or optimization',
|
|
compatibility: 'keep reading to preserve old backdrop geometry exactly',
|
|
},
|
|
{
|
|
id: 'spaces[].settings.fill_mode=glow',
|
|
selector: { path: ['spaces', '*', 'settings', 'fill_mode'], equals: 'glow' },
|
|
storage: 'House Plan server config',
|
|
type: "legacy enum value 'glow'",
|
|
default: 'none + Glow off',
|
|
level: 'space',
|
|
ui: 'replaced by fill_mode plus glow_enabled',
|
|
runtime: 'projects to data fill none and Glow on unless explicit glow_enabled wins',
|
|
introduced: 'pre-v1.61.0',
|
|
status: 'deprecated-read',
|
|
migration: "materialize fill_mode:'none' and effective glow_enabled on space edit or explicit optimization",
|
|
compatibility: 'backend and frontend accept the legacy token indefinitely for old dashboard bundles',
|
|
},
|
|
{
|
|
id: 'spaces[].rooms[].settings.fill_mode=glow',
|
|
selector: { path: ['spaces', '*', 'rooms', '*', 'settings', 'fill_mode'], equals: 'glow' },
|
|
storage: 'House Plan server config',
|
|
type: "legacy enum value 'glow'",
|
|
default: 'inherit data fill and space Glow',
|
|
level: 'room',
|
|
ui: 'replaced by inherited fill plus room glow override',
|
|
runtime: 'projects to inherited data fill and Glow on unless explicit room glow wins',
|
|
introduced: 'legacy/manual config',
|
|
status: 'deprecated-read',
|
|
migration: 'remove the fill token and materialize effective room glow on room edit or explicit optimization',
|
|
compatibility: 'backend and frontend accept the legacy token indefinitely',
|
|
},
|
|
{
|
|
id: 'spaces[].rooms[].settings.temp_min',
|
|
selector: { path: ['spaces', '*', 'rooms', '*', 'settings', 'temp_min'] },
|
|
storage: 'House Plan server config',
|
|
type: 'finite number | null',
|
|
default: 'unset/null (= inherit the lower comfort bound from the space)',
|
|
level: 'room',
|
|
ui: 'Room settings when the effective fill is Temperature',
|
|
runtime: 'roomTempRangeOf; affects temperature floor fill and opening tunnel fill only',
|
|
introduced: 'v1.73.0',
|
|
status: 'current',
|
|
migration: 'none — each absent/null bound inherits independently',
|
|
compatibility: 'older clients ignore the field and may erase it when rewriting complete room settings',
|
|
},
|
|
{
|
|
id: 'spaces[].rooms[].settings.temp_max',
|
|
selector: { path: ['spaces', '*', 'rooms', '*', 'settings', 'temp_max'] },
|
|
storage: 'House Plan server config',
|
|
type: 'finite number | null',
|
|
default: 'unset/null (= inherit the upper comfort bound from the space)',
|
|
level: 'room',
|
|
ui: 'Room settings when the effective fill is Temperature',
|
|
runtime: 'roomTempRangeOf; affects temperature floor fill and opening tunnel fill only',
|
|
introduced: 'v1.73.0',
|
|
status: 'current',
|
|
migration: 'none — each absent/null bound inherits independently',
|
|
compatibility: 'older clients ignore the field and may erase it when rewriting complete room settings',
|
|
},
|
|
{
|
|
id: 'spaces[].open_spans',
|
|
selector: { path: ['spaces', '*', 'open_spans'] },
|
|
storage: 'House Plan server config',
|
|
type: 'Array<{ a: Point; b: Point }>',
|
|
default: 'unset',
|
|
level: 'space',
|
|
ui: 'removed',
|
|
runtime: 'read-only compatibility projection to cm:0 contour atoms',
|
|
introduced: 'legacy virtual-boundary model',
|
|
status: 'migrate-on-write',
|
|
migration: 'convert covered wall_segments to cm:0 and remove after a structural write, Optimize or import',
|
|
compatibility: 'accepted only below model v9; canonical v9 rejects it',
|
|
},
|
|
{
|
|
id: 'spaces[].rooms[].open_to',
|
|
selector: { path: ['spaces', '*', 'rooms', '*', 'open_to'] },
|
|
storage: 'House Plan server config',
|
|
type: 'string[] | null',
|
|
default: 'unset',
|
|
level: 'room',
|
|
ui: 'removed',
|
|
runtime: 'read-only fallback projected to cm:0 when explicit open_spans are absent',
|
|
introduced: 'legacy virtual-boundary model',
|
|
status: 'deprecated-read',
|
|
migration: 'convert the proven shared wall_segments to cm:0 and remove after a structural write, Optimize or import',
|
|
compatibility: 'accepted only below model v9; canonical v9 rejects it',
|
|
},
|
|
{
|
|
id: 'spaces[].zero_wall_style',
|
|
selector: { path: ['spaces', '*', 'zero_wall_style'] },
|
|
storage: 'House Plan server config',
|
|
type: "'dashed' | 'solid'",
|
|
default: 'dashed',
|
|
level: 'space',
|
|
ui: 'space settings: zero-thickness walls',
|
|
runtime: 'dashed transmits Glow/sun; solid blocks both without adding a wall body',
|
|
introduced: 'model v9 / issue #306',
|
|
status: 'current',
|
|
migration: 'never materialized implicitly; missing and unknown values read as dashed',
|
|
compatibility: 'older clients ignore the field and do not understand the unified cm:0 light contract',
|
|
},
|
|
{
|
|
id: 'spaces[].wall_segments',
|
|
selector: { path: ['spaces', '*', 'wall_segments'] },
|
|
storage: 'House Plan server config',
|
|
type: 'Array<{ id: string; a: Point; b: Point; cm: number }>',
|
|
default: '[] after the first v8 structural write; required in model v9',
|
|
level: 'space',
|
|
ui: 'internal canonical wall identity',
|
|
runtime: 'authoritative contour-wall identity and thickness; cm:0 is a zero-thickness wall in model v9',
|
|
introduced: 'model v8 / issue #282',
|
|
status: 'current',
|
|
migration: 'materialize atomically on a structural write, Optimize or import',
|
|
compatibility: 'legacy walls[] remains a validated generated projection',
|
|
},
|
|
{
|
|
id: 'spaces[].rooms[].wall_ids',
|
|
selector: { path: ['spaces', '*', 'rooms', '*', 'wall_ids'] },
|
|
storage: 'House Plan server config',
|
|
type: 'string[]',
|
|
default: 'one id per poly edge in model v8',
|
|
level: 'room',
|
|
ui: 'internal canonical wall ownership',
|
|
runtime: 'maps every room polygon edge to one stable wall segment',
|
|
introduced: 'model v8 / issue #282',
|
|
status: 'current',
|
|
migration: 'materialize together with wall_segments',
|
|
compatibility: 'absent v7 rooms remain readable until a structural write',
|
|
},
|
|
{
|
|
id: 'spaces[].room_drafts[].segments[].id',
|
|
selector: { path: ['spaces', '*', 'room_drafts', '*', 'segments', '*', 'id'] },
|
|
storage: 'House Plan server config',
|
|
type: 'string',
|
|
default: 'deterministic id in model v8',
|
|
level: 'draft wall segment',
|
|
ui: 'internal draft identity',
|
|
runtime: 'preserves segment identity while an unfinished contour is continued',
|
|
introduced: 'model v8 / issue #282',
|
|
status: 'current',
|
|
migration: 'materialize together with wall_segments',
|
|
compatibility: 'optional on legacy draft reads',
|
|
},
|
|
{
|
|
id: 'spaces[].openings[].host=wall',
|
|
selector: { path: ['spaces', '*', 'openings', '*', 'host', 'kind'], equals: 'wall' },
|
|
storage: 'House Plan server config',
|
|
type: "{ kind: 'wall'; id: string; t: number }",
|
|
default: 'materialized by migration (#316 §3); may persist unhosted after degraded migration',
|
|
level: 'opening',
|
|
ui: 'internal opening ownership',
|
|
runtime: 'binds an opening to a stable contour-wall segment',
|
|
introduced: 'model v8 / issue #282',
|
|
status: 'current',
|
|
migration: 'auto-resolved per #316 §3.1-3.3; unhosted is the degraded terminal state',
|
|
compatibility: 'legacy unhosted openings remain readable before migration',
|
|
},
|
|
{
|
|
id: 'spaces[].decor[].width',
|
|
selector: { path: ['spaces', '*', 'decor', '*', 'width'] },
|
|
storage: 'House Plan server config',
|
|
type: 'number',
|
|
default: 'shape default',
|
|
level: 'decor object',
|
|
ui: 'replaced by width_cm',
|
|
runtime: 'read fallback for pixel-identical old rendering',
|
|
introduced: 'legacy decor model',
|
|
status: 'deprecated-read',
|
|
migration: 'convert to width_cm on object edit or explicit plan optimization',
|
|
compatibility: 'keep reading until migrated; canonical width_cm always wins',
|
|
},
|
|
{
|
|
id: 'spaces[].decor[text].size',
|
|
selector: { path: ['spaces', '*', 'decor', '*', 'size'], parent: { key: 'kind', equals: 'text' } },
|
|
storage: 'House Plan server config',
|
|
type: "'s' | 'm' | 'l'",
|
|
default: 'm',
|
|
level: 'decor text',
|
|
ui: 'removed',
|
|
runtime: 'legacy font multiplier fallback',
|
|
introduced: 'legacy decor text model',
|
|
status: 'deprecated-read',
|
|
migration: 'convert to size_cm on text edit or explicit plan optimization',
|
|
compatibility: 'keep reading so old labels remain pixel-identical',
|
|
},
|
|
{
|
|
id: 'spaces[].decor[text].scale',
|
|
selector: { path: ['spaces', '*', 'decor', '*', 'scale'], parent: { key: 'kind', equals: 'text' } },
|
|
storage: 'House Plan server config',
|
|
type: 'number',
|
|
default: '1',
|
|
level: 'decor text',
|
|
ui: 'replaced by size_cm',
|
|
runtime: 'legacy font multiplier fallback; explicit scale wins over size',
|
|
introduced: 'legacy decor text model',
|
|
status: 'deprecated-read',
|
|
migration: 'convert to size_cm on text edit or explicit plan optimization',
|
|
compatibility: 'keep reading so old labels remain pixel-identical',
|
|
},
|
|
...['entity', 'attr', 'unit'].map((field) => ({
|
|
id: `spaces[].decor[text].${field}`,
|
|
selector: { path: ['spaces', '*', 'decor', '*', field], parent: { key: 'kind', equals: 'text' } },
|
|
storage: 'House Plan server config',
|
|
type: field === 'entity' ? 'string | null' : 'string | null',
|
|
default: 'unset',
|
|
level: 'decor text',
|
|
ui: 'replaced by inline {entity[:attribute]} tokens',
|
|
runtime: 'legacy live-text fallback',
|
|
introduced: 'v1.59.0 beta-era live text',
|
|
status: 'deprecated-read',
|
|
migration: 'embed the reference into text on text edit or explicit plan optimization',
|
|
compatibility: 'backend accepts bounded values while old labels remain supported',
|
|
})),
|
|
{
|
|
id: 'settings.decor_default_style',
|
|
selector: { path: ['settings', 'decor_default_style'] },
|
|
storage: 'House Plan server config',
|
|
type: '{ color?, opacity?, width_cm?, fill?, fill_color?, fill_opacity? }',
|
|
default: 'unset (absence IS the factory default)',
|
|
level: 'global',
|
|
ui: 'Background editor main-toolbar picker',
|
|
runtime: 'seeds the editor default decor style once per page (#377)',
|
|
introduced: 'v1.69.0',
|
|
status: 'current',
|
|
migration: 'none — reset to default removes the key',
|
|
compatibility: 'optional; partial keys inherit the factory default per field',
|
|
},
|
|
{
|
|
id: "spaces[].decor[]<kind='furniture'>",
|
|
selector: { path: ['spaces', '*', 'decor', '*', 'kind'], equals: 'furniture' },
|
|
storage: 'House Plan server config',
|
|
type: "decor item variant: symbol + box (x, y, w, h, angle)",
|
|
default: 'n/a',
|
|
level: 'space',
|
|
ui: 'Background editor furniture tool (#159/#359)',
|
|
runtime: 'plan-art rendering, placement preview, stroke zoom (#361)',
|
|
introduced: 'v1.69.0',
|
|
status: 'current',
|
|
migration: 'none',
|
|
compatibility: 'unknown symbols render as generic box; assets ship with the bundle',
|
|
},
|
|
{
|
|
id: "spaces[].decor[]<kind='image'>",
|
|
selector: { path: ['spaces', '*', 'decor', '*', 'kind'], equals: 'image' },
|
|
storage: 'House Plan server config + content-addressed houseplan/assets store',
|
|
type: 'decor item variant: asset_id + box (x, y, w, h, angle, opacity, flip_h, flip_v)',
|
|
default: 'n/a',
|
|
level: 'space',
|
|
ui: 'Background editor Image palette (#51)',
|
|
runtime: 'full/static decor projection through verified signed content',
|
|
introduced: 'v1.71.0',
|
|
status: 'current',
|
|
migration: 'none — additive record; file bytes are never embedded in config',
|
|
compatibility: 'capability-gated writes; missing bytes keep an editor repair placeholder',
|
|
},
|
|
{
|
|
id: "spaces[].openings[].host=partition",
|
|
selector: { path: ['spaces', '*', 'openings', '*', 'host', 'kind'], equals: 'partition' },
|
|
storage: 'House Plan server config',
|
|
type: "{ kind: 'partition', id, t }",
|
|
default: 'n/a',
|
|
level: 'space',
|
|
ui: 'Plan editor: openings hosted on partitions (model v9, v1.68)',
|
|
runtime: 'opening geometry, junction limits, glow cuts',
|
|
introduced: 'v1.68.0',
|
|
status: 'current',
|
|
migration: 'v9 migration resolves opening-vs-zero-wall conflicts (#316)',
|
|
compatibility: 'v8 configs without partition hosts stay valid',
|
|
},
|
|
]);
|
|
|
|
export const CONFIG_FIELD_STATUSES = Object.freeze([
|
|
'current',
|
|
'decision-required',
|
|
'deprecated-read',
|
|
'migrate-on-write',
|
|
'migrate-on-settings-save',
|
|
'drop-on-validation',
|
|
]);
|